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,62 @@
package transformers
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
)
type chainedTransformer struct {
Transformer
components []*Transformer
}
func (ch *chainedTransformer) visit(node *ast.Node) *ast.Node {
if node.Kind != ast.KindSourceFile {
panic("Chained transform passed non-sourcefile initial node")
}
result := node.AsSourceFile()
for _, t := range ch.components {
result = t.TransformSourceFile(result)
}
return result.AsNode()
}
type TransformOptions struct {
Context *printer.EmitContext
CompilerOptions *core.CompilerOptions
Resolver binder.ReferenceResolver
EmitResolver printer.EmitResolver
GetEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
}
type TransformerFactory = func(opt *TransformOptions) *Transformer
// Chains transforms in left-to-right order, running them one at a time in order (as opposed to interleaved at each node)
// - the resulting combined transform only operates on SourceFile nodes
func Chain(transforms ...TransformerFactory) TransformerFactory {
if len(transforms) < 2 {
if len(transforms) == 0 {
panic("Expected some number of transforms to chain, but got none")
}
return transforms[0]
}
return func(opt *TransformOptions) *Transformer {
constructed := make([]*Transformer, 0, len(transforms))
for _, t := range transforms {
// TODO: flatten nested chains?
if result := t(opt); result != nil {
constructed = append(constructed, result)
}
}
switch len(constructed) {
case 0:
return nil
case 1:
return constructed[0]
}
ch := &chainedTransformer{components: constructed}
return ch.NewTransformer(ch.visit, opt.Context)
}
}

View File

@@ -0,0 +1,735 @@
package declarations
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
)
type GetSymbolAccessibilityDiagnostic = func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic
type SymbolAccessibilityDiagnostic struct {
errorNode *ast.Node
diagnosticMessage *diagnostics.Message
typeName *ast.Node
}
func wrapSimpleDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic {
return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
diagnosticMessage := selector(node, symbolAccessibilityResult)
if diagnosticMessage == nil {
return nil
}
return &SymbolAccessibilityDiagnostic{
errorNode: node,
diagnosticMessage: diagnosticMessage,
typeName: ast.GetNameOfDeclaration(node),
}
}
}
func wrapNamedDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic {
return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
diagnosticMessage := selector(node, symbolAccessibilityResult)
if diagnosticMessage == nil {
return nil
}
name := ast.GetNameOfDeclaration(node)
return &SymbolAccessibilityDiagnostic{
errorNode: name,
diagnosticMessage: diagnosticMessage,
typeName: name,
}
}
}
func wrapFallbackErrorDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic {
return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
diagnosticMessage := selector(node, symbolAccessibilityResult)
if diagnosticMessage == nil {
return nil
}
errorNode := ast.GetNameOfDeclaration(node)
if errorNode == nil {
errorNode = node
}
return &SymbolAccessibilityDiagnostic{
errorNode: errorNode,
diagnosticMessage: diagnosticMessage,
}
}
}
func selectDiagnosticBasedOnModuleName(symbolAccessibilityResult printer.SymbolAccessibilityResult, moduleNotNameable *diagnostics.Message, privateModule *diagnostics.Message, nonModule *diagnostics.Message) *diagnostics.Message {
if len(symbolAccessibilityResult.ErrorModuleName) > 0 {
if symbolAccessibilityResult.Accessibility == printer.SymbolAccessibilityCannotBeNamed {
return moduleNotNameable
}
return privateModule
}
return nonModule
}
func selectDiagnosticBasedOnModuleNameNoNameCheck(symbolAccessibilityResult printer.SymbolAccessibilityResult, privateModule *diagnostics.Message, nonModule *diagnostics.Message) *diagnostics.Message {
if len(symbolAccessibilityResult.ErrorModuleName) > 0 {
return privateModule
}
return nonModule
}
func createGetSymbolAccessibilityDiagnosticForNodeName(node *ast.Node) GetSymbolAccessibilityDiagnostic {
if ast.IsSetAccessorDeclaration(node) || ast.IsGetAccessorDeclaration(node) {
return wrapSimpleDiagnosticSelector(node, getAccessorNameVisibilityDiagnosticMessage)
} else if ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) {
return wrapSimpleDiagnosticSelector(node, getMethodNameVisibilityDiagnosticMessage)
} else {
return createGetSymbolAccessibilityDiagnosticForNode(node)
}
}
func getAccessorNameVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
if ast.IsStatic(node) {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1,
)
} else if node.Parent.Kind == ast.KindClassDeclaration {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1,
)
} else {
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1,
)
}
}
func getMethodNameVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
if ast.IsStatic(node) {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1,
)
} else if node.Parent.Kind == ast.KindClassDeclaration {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1,
)
} else {
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1,
)
}
}
func createGetSymbolAccessibilityDiagnosticForNode(node *ast.Node) GetSymbolAccessibilityDiagnostic {
if ast.IsVariableDeclaration(node) || ast.IsPropertyDeclaration(node) || ast.IsPropertySignatureDeclaration(node) || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node) || ast.IsBinaryExpression(node) || ast.IsBindingElement(node) || ast.IsConstructorDeclaration(node) {
return wrapSimpleDiagnosticSelector(node, getVariableDeclarationTypeVisibilityDiagnosticMessage)
} else if ast.IsSetAccessorDeclaration(node) || ast.IsGetAccessorDeclaration(node) {
return wrapNamedDiagnosticSelector(node, getAccessorDeclarationTypeVisibilityDiagnosticMessage)
} else if ast.IsConstructSignatureDeclaration(node) || ast.IsCallSignatureDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsFunctionDeclaration(node) || ast.IsIndexSignatureDeclaration(node) {
return wrapFallbackErrorDiagnosticSelector(node, getReturnTypeVisibilityDiagnosticMessage)
} else if ast.IsParameterDeclaration(node) {
if ast.IsParameterPropertyDeclaration(node, node.Parent) && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsPrivate) {
return wrapSimpleDiagnosticSelector(node, getVariableDeclarationTypeVisibilityDiagnosticMessage)
}
return wrapSimpleDiagnosticSelector(node, getParameterDeclarationTypeVisibilityDiagnosticMessage)
} else if ast.IsTypeParameterDeclaration(node) {
return wrapSimpleDiagnosticSelector(node, getTypeParameterConstraintVisibilityDiagnosticMessage)
} else if ast.IsExpressionWithTypeArguments(node) {
// unique node selection behavior, inline closure
return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
var diagnosticMessage *diagnostics.Message
// Heritage clause is written by user so it can always be named
if ast.IsClassDeclaration(node.Parent.Parent) {
// Class or Interface implemented/extended is inaccessible
if ast.IsHeritageClause(node.Parent) && node.Parent.AsHeritageClause().Token == ast.KindImplementsKeyword {
diagnosticMessage = diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1
} else {
if node.Parent.Parent.Name() != nil {
diagnosticMessage = diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1
} else {
diagnosticMessage = diagnostics.X_extends_clause_of_exported_class_has_or_is_using_private_name_0
}
}
} else {
// interface is inaccessible
diagnosticMessage = diagnostics.X_extends_clause_of_exported_interface_0_has_or_is_using_private_name_1
}
return &SymbolAccessibilityDiagnostic{
diagnosticMessage: diagnosticMessage,
errorNode: node,
typeName: ast.GetNameOfDeclaration(node.Parent.Parent),
}
}
} else if ast.IsImportEqualsDeclaration(node) {
return wrapSimpleDiagnosticSelector(node, func(_ *ast.Node, _ printer.SymbolAccessibilityResult) *diagnostics.Message {
return diagnostics.Import_declaration_0_is_using_private_name_1
})
} else if ast.IsTypeAliasDeclaration(node) || ast.IsJSTypeAliasDeclaration(node) {
// unique node selection behavior, inline closure
return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
diagnosticMessage := selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1_from_module_2,
diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1,
)
errorNode := node.Type()
typeName := node.Name()
return &SymbolAccessibilityDiagnostic{
errorNode: errorNode,
diagnosticMessage: diagnosticMessage,
typeName: typeName,
}
}
} else if ast.IsCallExpression(node) {
// JS object.defineProperty call
// unique node selection behavior, inline closure
return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic {
diagnosticMessage := selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2,
diagnostics.Exported_variable_0_has_or_is_using_private_name_1,
)
errorNode := node.Arguments()[1]
typeName := node.Arguments()[1]
return &SymbolAccessibilityDiagnostic{
errorNode: errorNode,
diagnosticMessage: diagnosticMessage,
typeName: typeName,
}
}
} else {
panic("Attempted to set a declaration diagnostic context for unhandled node kind: " + node.Kind.String())
}
}
func getVariableDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
if node.Kind == ast.KindVariableDeclaration || node.Kind == ast.KindBindingElement {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2,
diagnostics.Exported_variable_0_has_or_is_using_private_name_1,
)
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
// The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all.
} else if node.Kind == ast.KindPropertyDeclaration || node.Kind == ast.KindPropertyAccessExpression || node.Kind == ast.KindElementAccessExpression || node.Kind == ast.KindBinaryExpression || node.Kind == ast.KindPropertySignature ||
(node.Kind == ast.KindParameter && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsPrivate)) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if ast.IsStatic(node) {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1,
)
} else if node.Parent.Kind == ast.KindClassDeclaration || node.Kind == ast.KindParameter {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1,
)
} else {
// Interfaces cannot have types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1,
)
}
}
return nil // TODO: Audit behavior - should this panic? potentially silent error state in strada
}
func getAccessorDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
if node.Kind == ast.KindSetAccessor {
// Getters can infer the return type from the returned expression, but setters cannot, so the
// "_from_external_module_1_but_cannot_be_named" case cannot occur.
if ast.IsStatic(node) {
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1,
)
} else {
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1,
)
}
} else {
if ast.IsStatic(node) {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1,
)
} else {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1,
)
}
}
}
func getReturnTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
switch node.Kind {
case ast.KindConstructSignature:
// Interfaces cannot have return types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0,
)
case ast.KindCallSignature:
// Interfaces cannot have return types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0,
)
case ast.KindIndexSignature:
// Interfaces cannot have return types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0,
)
case ast.KindMethodDeclaration, ast.KindMethodSignature:
if ast.IsStatic(node) {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named,
diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0,
)
} else if node.Parent.Kind == ast.KindClassDeclaration {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named,
diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0,
)
} else {
// Interfaces cannot have return types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0,
)
}
case ast.KindFunctionDeclaration:
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named,
diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1,
diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0,
)
default:
panic("This is unknown kind for signature: " + node.Kind.String())
}
}
func getParameterDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
switch node.Parent.Kind {
case ast.KindConstructor:
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1,
)
case ast.KindConstructSignature, ast.KindConstructorType:
// Interfaces cannot have parameter types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1,
)
case ast.KindCallSignature:
// Interfaces cannot have parameter types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1,
)
case ast.KindIndexSignature:
// Interfaces cannot have parameter types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1,
)
case ast.KindMethodDeclaration, ast.KindMethodSignature:
if ast.IsStatic(node.Parent) {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1,
)
} else if node.Parent.Parent.Kind == ast.KindClassDeclaration {
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1,
)
} else {
// Interfaces cannot have parameter types that cannot be named
return selectDiagnosticBasedOnModuleNameNoNameCheck(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1,
)
}
case ast.KindFunctionDeclaration, ast.KindFunctionType:
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1,
)
case ast.KindSetAccessor, ast.KindGetAccessor:
return selectDiagnosticBasedOnModuleName(
symbolAccessibilityResult,
diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named,
diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_private_module_2,
diagnostics.Parameter_0_of_accessor_has_or_is_using_private_name_1,
)
default:
panic("Unknown parent for parameter: " + node.Parent.Kind.String())
}
}
func getTypeParameterConstraintVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message {
// Type parameter constraints are named by user so we should always be able to name it
switch node.Parent.Kind {
case ast.KindClassDeclaration:
return diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1
case ast.KindInterfaceDeclaration:
return diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1
case ast.KindMappedType:
return diagnostics.Type_parameter_0_of_exported_mapped_object_type_is_using_private_name_1
case ast.KindConstructorType, ast.KindConstructSignature:
return diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1
case ast.KindCallSignature:
return diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1
case ast.KindMethodDeclaration, ast.KindMethodSignature:
if ast.IsStatic(node.Parent) {
return diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1
} else if node.Parent.Parent.Kind == ast.KindClassDeclaration {
return diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1
} else {
return diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1
}
case ast.KindFunctionType, ast.KindFunctionDeclaration:
return diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1
case ast.KindInferType:
return diagnostics.Extends_clause_for_inferred_type_0_has_or_is_using_private_name_1
case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
return diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1
default:
panic("This is unknown parent for type parameter: " + node.Parent.Kind.String())
}
}
func getRelatedSuggestionByDeclarationKind(kind ast.Kind) *diagnostics.Message {
switch kind {
case ast.KindArrowFunction:
return diagnostics.Add_a_return_type_to_the_function_expression
case ast.KindFunctionExpression:
return diagnostics.Add_a_return_type_to_the_function_expression
case ast.KindMethodDeclaration:
return diagnostics.Add_a_return_type_to_the_method
case ast.KindGetAccessor:
return diagnostics.Add_a_return_type_to_the_get_accessor_declaration
case ast.KindSetAccessor:
return diagnostics.Add_a_type_to_parameter_of_the_set_accessor_declaration
case ast.KindFunctionDeclaration:
return diagnostics.Add_a_return_type_to_the_function_declaration
case ast.KindConstructSignature:
return diagnostics.Add_a_return_type_to_the_function_declaration
case ast.KindParameter:
return diagnostics.Add_a_type_annotation_to_the_parameter_0
case ast.KindVariableDeclaration:
return diagnostics.Add_a_type_annotation_to_the_variable_0
case ast.KindPropertyDeclaration:
return diagnostics.Add_a_type_annotation_to_the_property_0
case ast.KindPropertySignature:
return diagnostics.Add_a_type_annotation_to_the_property_0
case ast.KindExportAssignment:
return diagnostics.Move_the_expression_in_default_export_to_a_variable_and_add_a_type_annotation_to_it
default:
return nil
}
}
func getErrorByDeclarationKind(kind ast.Kind) *diagnostics.Message {
switch kind {
case ast.KindFunctionExpression:
return diagnostics.Function_must_have_an_explicit_return_type_annotation_with_isolatedDeclarations
case ast.KindFunctionDeclaration:
return diagnostics.Function_must_have_an_explicit_return_type_annotation_with_isolatedDeclarations
case ast.KindArrowFunction:
return diagnostics.Function_must_have_an_explicit_return_type_annotation_with_isolatedDeclarations
case ast.KindMethodDeclaration:
return diagnostics.Method_must_have_an_explicit_return_type_annotation_with_isolatedDeclarations
case ast.KindConstructSignature:
return diagnostics.Method_must_have_an_explicit_return_type_annotation_with_isolatedDeclarations
case ast.KindGetAccessor:
return diagnostics.At_least_one_accessor_must_have_an_explicit_type_annotation_with_isolatedDeclarations
case ast.KindSetAccessor:
return diagnostics.At_least_one_accessor_must_have_an_explicit_type_annotation_with_isolatedDeclarations
case ast.KindParameter:
return diagnostics.Parameter_must_have_an_explicit_type_annotation_with_isolatedDeclarations
case ast.KindVariableDeclaration:
return diagnostics.Variable_must_have_an_explicit_type_annotation_with_isolatedDeclarations
case ast.KindPropertyDeclaration:
return diagnostics.Property_must_have_an_explicit_type_annotation_with_isolatedDeclarations
case ast.KindPropertySignature:
return diagnostics.Property_must_have_an_explicit_type_annotation_with_isolatedDeclarations
case ast.KindComputedPropertyName:
return diagnostics.Computed_property_names_on_class_or_object_literals_cannot_be_inferred_with_isolatedDeclarations
case ast.KindSpreadAssignment:
return diagnostics.Objects_that_contain_spread_assignments_can_t_be_inferred_with_isolatedDeclarations
case ast.KindShorthandPropertyAssignment:
return diagnostics.Objects_that_contain_shorthand_properties_can_t_be_inferred_with_isolatedDeclarations
case ast.KindArrayLiteralExpression:
return diagnostics.Only_const_arrays_can_be_inferred_with_isolatedDeclarations
case ast.KindExportAssignment:
return diagnostics.Default_exports_can_t_be_inferred_with_isolatedDeclarations
case ast.KindSpreadElement:
return diagnostics.Arrays_with_spread_elements_can_t_inferred_with_isolatedDeclarations
default:
return nil
}
}
func isDeclarationEnoughForErrors(node *ast.Node) bool {
return ast.IsExportAssignment(node) || ast.IsStatement(node) || ast.IsVariableDeclaration(node) || ast.IsPropertyDeclaration(node) || ast.IsParameterDeclaration(node)
}
func isFunctionLikeAndNotConstructor(node *ast.Node) bool {
return ast.IsFunctionLikeDeclaration(node) && !ast.IsConstructorDeclaration(node)
}
func findNearestDeclaration(node *ast.Node) *ast.Node {
result := ast.FindAncestor(node, isDeclarationEnoughForErrors)
if result == nil {
return nil
}
if ast.IsExportAssignment(result) {
return result
}
if ast.IsReturnStatement(result) {
return ast.FindAncestor(result, isFunctionLikeAndNotConstructor)
}
if ast.IsStatement(result) {
return nil
}
return result
}
func createEntityInTypeNodeError(node *ast.Node) *ast.Diagnostic {
diag := createDiagnosticForNode(node, diagnostics.Type_containing_private_name_0_can_t_be_used_with_isolatedDeclarations, scanner.GetTextOfNode(node))
addParentDeclarationRelatedInfo(node, diag)
return diag
}
func addParentDeclarationRelatedInfo(node *ast.Node, diag *ast.Diagnostic) {
parentDeclaration := findNearestDeclaration(node)
if parentDeclaration == nil {
return
}
targetStr := ""
if !ast.IsExportAssignment(parentDeclaration) && parentDeclaration.Name() != nil {
targetStr = scanner.GetTextOfNode(parentDeclaration.Name())
}
diag.AddRelatedInfo(createDiagnosticForNode(parentDeclaration, getRelatedSuggestionByDeclarationKind(parentDeclaration.Kind), targetStr))
}
func createAccessorTypeError(node *ast.Node) *ast.Diagnostic {
allDeclarations := ast.GetAllAccessorDeclarationsForDeclaration(node, node.Symbol().Declarations)
getAccessor := allDeclarations.GetAccessor
setAccessor := allDeclarations.SetAccessor
targetNode := node
if ast.IsSetAccessorDeclaration(node) && len(node.Parameters()) > 0 {
targetNode = node.Parameters()[0]
}
diag := createDiagnosticForNode(targetNode, getErrorByDeclarationKind(node.Kind))
if setAccessor != nil {
diag.AddRelatedInfo(createDiagnosticForNode(setAccessor.AsNode(), getRelatedSuggestionByDeclarationKind(setAccessor.Kind)))
}
if getAccessor != nil {
diag.AddRelatedInfo(createDiagnosticForNode(getAccessor.AsNode(), getRelatedSuggestionByDeclarationKind(getAccessor.Kind)))
}
return diag
}
func createObjectLiteralError(node *ast.Node) *ast.Diagnostic {
diag := createDiagnosticForNode(node, getErrorByDeclarationKind(node.Kind))
addParentDeclarationRelatedInfo(node, diag)
return diag
}
func createArrayLiteralError(node *ast.Node) *ast.Diagnostic {
diag := createDiagnosticForNode(node, getErrorByDeclarationKind(node.Kind))
addParentDeclarationRelatedInfo(node, diag)
return diag
}
func createReturnTypeError(node *ast.Node) *ast.Diagnostic {
diag := createDiagnosticForNode(node, getErrorByDeclarationKind(node.Kind))
addParentDeclarationRelatedInfo(node, diag)
diag.AddRelatedInfo(createDiagnosticForNode(node, getRelatedSuggestionByDeclarationKind(node.Kind)))
return diag
}
func createBindingElementError(node *ast.Node) *ast.Diagnostic {
return createDiagnosticForNode(node, diagnostics.Binding_elements_can_t_be_exported_directly_with_isolatedDeclarations)
}
func createVariableOrPropertyError(node *ast.Node) *ast.Diagnostic {
diag := createDiagnosticForNode(node, getErrorByDeclarationKind(node.Kind))
diag.AddRelatedInfo(createDiagnosticForNode(node, getRelatedSuggestionByDeclarationKind(node.Kind), scanner.GetTextOfNode(node.Name())))
return diag
}
func createExpressionError(node *ast.Node) *ast.Diagnostic {
return createExpressionErrorEx(node, nil)
}
func createClassExpressionError(node *ast.Node) *ast.Diagnostic {
return createExpressionErrorEx(node, diagnostics.Inference_from_class_expressions_is_not_supported_with_isolatedDeclarations)
}
func isParentForIDDIagnostic(node *ast.Node) ast.FindAncestorResult {
if ast.IsExportAssignment(node) {
return ast.FindAncestorTrue
}
if ast.IsStatement(node) {
return ast.FindAncestorQuit
}
return ast.ToFindAncestorResult(!ast.IsParenthesizedExpression(node) && !ast.IsAssertionExpression(node))
}
func createExpressionErrorEx(node *ast.Node, diagnosticMessage *diagnostics.Message) *ast.Diagnostic {
parentDeclaration := findNearestDeclaration(node)
if parentDeclaration == nil {
if diagnosticMessage == nil {
diagnosticMessage = diagnostics.Expression_type_can_t_be_inferred_with_isolatedDeclarations
}
return createDiagnosticForNode(node, diagnosticMessage)
}
targetStr := ""
if !ast.IsExportAssignment(parentDeclaration) && parentDeclaration.Name() != nil {
targetStr = scanner.GetTextOfNode(parentDeclaration.Name())
}
parent := ast.FindAncestorOrQuit(node.Parent, isParentForIDDIagnostic)
if parentDeclaration == parent {
if diagnosticMessage == nil {
diagnosticMessage = getErrorByDeclarationKind(parentDeclaration.Kind)
}
diag := createDiagnosticForNode(node, diagnosticMessage)
diag.AddRelatedInfo(createDiagnosticForNode(parentDeclaration, getRelatedSuggestionByDeclarationKind(parentDeclaration.Kind), targetStr))
return diag
}
if diagnosticMessage == nil {
diagnosticMessage = diagnostics.Expression_type_can_t_be_inferred_with_isolatedDeclarations
}
diag := createDiagnosticForNode(node, diagnosticMessage)
diag.AddRelatedInfo(createDiagnosticForNode(parentDeclaration, getRelatedSuggestionByDeclarationKind(parentDeclaration.Kind), targetStr))
diag.AddRelatedInfo(createDiagnosticForNode(node, diagnostics.Add_satisfies_and_a_type_assertion_to_this_expression_satisfies_T_as_T_to_make_the_type_explicit))
return diag
}
func createGetIsolatedDeclarationErrors(resolver printer.EmitResolver) func(node *ast.Node) *ast.Diagnostic {
createParameterError := func(node *ast.Node) *ast.Diagnostic {
if ast.IsSetAccessorDeclaration(node.Parent) {
return createAccessorTypeError(node.Parent)
}
addUndefined := resolver.RequiresAddingImplicitUndefinedUnsafe(node, nil, nil) // skip checker lock - node builder will already have one
if !addUndefined && node.Initializer() != nil {
return createExpressionError(node.Initializer())
}
message := getErrorByDeclarationKind(node.Kind)
if addUndefined {
message = diagnostics.Declaration_emit_for_this_parameter_requires_implicitly_adding_undefined_to_its_type_This_is_not_supported_with_isolatedDeclarations
}
diag := createDiagnosticForNode(node, message)
targetStr := scanner.GetTextOfNode(node.Name())
diag.AddRelatedInfo(createDiagnosticForNode(node, getRelatedSuggestionByDeclarationKind(node.Kind), targetStr))
return diag
}
return func(node *ast.Node) *ast.Diagnostic {
heritageClause := ast.FindAncestor(node, ast.IsHeritageClause)
if heritageClause != nil {
return createDiagnosticForNode(node, diagnostics.Extends_clause_can_t_contain_an_expression_with_isolatedDeclarations)
}
if ast.IsPartOfTypeNode(node) || ast.IsTypeQueryNode(node) {
return createEntityInTypeNodeError(node)
}
if ast.IsEntityName(node) || ast.IsEntityNameExpression(node) {
return createEntityInTypeNodeError(node)
}
switch node.Kind {
case ast.KindGetAccessor, ast.KindSetAccessor:
return createAccessorTypeError(node)
case ast.KindComputedPropertyName, ast.KindShorthandPropertyAssignment, ast.KindSpreadAssignment:
return createObjectLiteralError(node)
case ast.KindArrayLiteralExpression, ast.KindSpreadElement:
return createArrayLiteralError(node)
case ast.KindMethodDeclaration, ast.KindConstructSignature, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindFunctionDeclaration:
return createReturnTypeError(node)
case ast.KindBindingElement:
return createBindingElementError(node)
case ast.KindPropertyDeclaration, ast.KindVariableDeclaration:
return createVariableOrPropertyError(node)
case ast.KindParameter:
return createParameterError(node)
case ast.KindPropertyAssignment:
return createExpressionError(node.Initializer())
case ast.KindClassExpression:
return createClassExpressionError(node)
default:
return createExpressionError(node)
}
}
}

View File

@@ -0,0 +1,252 @@
package declarations
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/checker"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
)
type SymbolTrackerImpl struct {
resolver printer.EmitResolver
state *SymbolTrackerSharedState
host DeclarationEmitHost
fallbackStack []*ast.Node
// For detecting class expression self-references during member serialization.
// When set, TrackSymbol will record usage without reporting accessibility errors.
watchedClassSymbol *ast.Symbol
classSymbolTracked bool
getIsolatedDeclarationError func(node *ast.Node) *ast.Diagnostic
}
// PopErrorFallbackNode implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) PopErrorFallbackNode() {
s.fallbackStack = s.fallbackStack[:len(s.fallbackStack)-1]
}
// PushErrorFallbackNode implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) {
s.fallbackStack = append(s.fallbackStack, node)
}
// ReportCyclicStructureError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportCyclicStructureError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_a_type_with_a_cyclic_structure_which_cannot_be_trivially_serialized_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback()))
}
}
// ReportInaccessibleThisError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportInaccessibleThisError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), "this"))
}
}
// ReportInaccessibleUniqueSymbolError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), "unique symbol"))
}
}
func (s *SymbolTrackerImpl) isBoundExpando(node *ast.Node) bool {
if !(ast.IsExpandoPropertyDeclaration(node) && ast.IsPropertyAccessExpression(node.AsBinaryExpression().Left)) {
return false
}
ref := s.resolver.GetReferencedValueDeclarationUnsafe(ast.GetLeftmostExpression(node.AsBinaryExpression().Left, true))
if ref == nil {
return false
}
return s.resolver.IsExpandoFunctionDeclarationUnsafe(ref)
}
func (s *SymbolTrackerImpl) isChildOfBoundExpando(node *ast.Node) bool {
return ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult {
if ast.IsSourceFile(n) || ast.IsBlock(n) {
return ast.FindAncestorQuit
}
return ast.ToFindAncestorResult(s.isBoundExpando(n))
}) != nil
}
// ReportInferenceFallback implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) {
if !s.state.isolatedDeclarations {
return
}
if ast.GetSourceFileOfNode(node) != s.state.currentSourceFile {
return // Nested error on a declaration in another file - ignore, will be reemitted if file is in the output file set
}
if s.state.resolver.IsExpandoFunctionDeclarationUnsafe(node) { // within a node builder call that should already lock the checker, use the unsafe call
s.state.reportExpandoFunctionErrors(node)
}
if !s.isChildOfBoundExpando(node) { // expando props get an error when their host is visited by the above, this prevents a follow-on error on a non-inferrable expression
s.state.addDiagnostic(s.getIsolatedDeclarationError(node))
}
}
// ReportLikelyUnsafeImportRequiredError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string, symbolName string) {
location := s.errorLocation()
if location != nil {
if symbolName != "" {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_cannot_be_named_without_a_reference_to_2_from_1_This_is_likely_not_portable_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), specifier, symbolName))
} else {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_cannot_be_named_without_a_reference_to_1_This_is_likely_not_portable_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), specifier))
}
}
}
// ReportNonSerializableProperty implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_type_of_this_node_cannot_be_serialized_because_its_property_0_cannot_be_serialized, propertyName))
}
}
// ReportNonlocalAugmentation implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) {
primaryDeclaration := core.Find(parentSymbol.Declarations, func(d *ast.Node) bool { return ast.GetSourceFileOfNode(d) == containingFile })
augmentingDeclarations := core.Filter(augmentingSymbol.Declarations, func(d *ast.Node) bool { return ast.GetSourceFileOfNode(d) != containingFile })
if primaryDeclaration != nil && len(augmentingDeclarations) > 0 {
for _, augmentations := range augmentingDeclarations {
diag := createDiagnosticForNode(augmentations, diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized)
related := createDiagnosticForNode(primaryDeclaration, diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file)
diag.AddRelatedInfo(related)
s.state.addDiagnostic(diag)
}
}
}
// ReportPrivateInBaseOfClassExpression implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) {
location := s.errorLocation()
if location != nil {
diag := createDiagnosticForNode(location, diagnostics.Property_0_of_exported_anonymous_class_type_may_not_be_private_or_protected, propertyName)
if ast.IsVariableDeclaration(location.Parent) {
related := createDiagnosticForNode(location, diagnostics.Add_a_type_annotation_to_the_variable_0, s.errorDeclarationNameWithFallback())
diag.AddRelatedInfo(related)
}
s.state.addDiagnostic(diag)
}
}
// ReportTruncationError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportTruncationError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_this_node_exceeds_the_maximum_length_the_compiler_will_serialize_An_explicit_type_annotation_is_needed))
}
}
func (s *SymbolTrackerImpl) errorFallbackNode() *ast.Node {
if len(s.fallbackStack) >= 1 {
return s.fallbackStack[len(s.fallbackStack)-1]
}
return nil
}
func (s *SymbolTrackerImpl) errorLocation() *ast.Node {
location := s.state.errorNameNode
if location == nil {
location = s.errorFallbackNode()
}
return location
}
func (s *SymbolTrackerImpl) errorDeclarationNameWithFallback() string {
if s.state.errorNameNode != nil {
return scanner.DeclarationNameToString(s.state.errorNameNode)
}
if s.errorFallbackNode() != nil && ast.GetNameOfDeclaration(s.errorFallbackNode()) != nil {
return scanner.DeclarationNameToString(ast.GetNameOfDeclaration(s.errorFallbackNode()))
}
if s.errorFallbackNode() != nil && ast.IsExportAssignment(s.errorFallbackNode()) {
if s.errorFallbackNode().AsExportAssignment().IsExportEquals {
return "export="
}
return "default"
}
return "(Missing)" // same fallback declarationNameToString uses when node is zero-width (ie, nameless)
}
// TrackSymbol implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool {
if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 {
return false
}
// When watching for a class expression symbol, record its usage without
// reporting accessibility errors — the caller will handle visibility by
// wrapping the class in a namespace.
if s.watchedClassSymbol != nil && symbol == s.watchedClassSymbol {
s.classSymbolTracked = true
return false
}
issuedDiagnostic := s.handleSymbolAccessibilityError(s.resolver.IsSymbolAccessible(symbol, enclosingDeclaration, meaning /*shouldComputeAliasToMarkVisible*/, true))
return issuedDiagnostic
}
func (s *SymbolTrackerImpl) handleSymbolAccessibilityError(symbolAccessibilityResult printer.SymbolAccessibilityResult) bool {
if symbolAccessibilityResult.Accessibility == printer.SymbolAccessibilityAccessible {
// Add aliases back onto the possible imports list if they're not there so we can try them again with updated visibility info
if len(symbolAccessibilityResult.AliasesToMakeVisible) > 0 {
for _, ref := range symbolAccessibilityResult.AliasesToMakeVisible {
s.state.lateMarkedStatements = core.AppendIfUnique(s.state.lateMarkedStatements, ref)
}
}
// TODO: Do all these accessibility checks inside/after the first pass in the checker when declarations are enabled, if possible
// The checker should issue errors on unresolvable names, skip the declaration emit error for using a private/unreachable name for those
} else if symbolAccessibilityResult.Accessibility != printer.SymbolAccessibilityNotResolved {
// Report error
errorInfo := s.state.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult)
if errorInfo != nil {
info := *errorInfo
diagNode := symbolAccessibilityResult.ErrorNode
if diagNode == nil {
diagNode = errorInfo.errorNode
}
if info.typeName != nil {
s.state.addDiagnostic(createDiagnosticForNode(diagNode, info.diagnosticMessage, scanner.GetTextOfNode(info.typeName), symbolAccessibilityResult.ErrorSymbolName, symbolAccessibilityResult.ErrorModuleName))
} else {
s.state.addDiagnostic(createDiagnosticForNode(diagNode, info.diagnosticMessage, symbolAccessibilityResult.ErrorSymbolName, symbolAccessibilityResult.ErrorModuleName))
}
return true
}
}
return false
}
func createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
return checker.NewDiagnosticForNode(node, message, args...)
}
type SymbolTrackerSharedState struct {
lateMarkedStatements []*ast.Node
diagnostics []*ast.Diagnostic
getSymbolAccessibilityDiagnostic GetSymbolAccessibilityDiagnostic
errorNameNode *ast.Node
isolatedDeclarations bool
stripInternal bool
currentSourceFile *ast.SourceFile
resolver printer.EmitResolver
reportExpandoFunctionErrors func(node *ast.Node)
}
func (s *SymbolTrackerSharedState) addDiagnostic(diag *ast.Diagnostic) {
s.diagnostics = append(s.diagnostics, diag)
}
func NewSymbolTracker(host DeclarationEmitHost, resolver printer.EmitResolver, state *SymbolTrackerSharedState) *SymbolTrackerImpl {
tracker := &SymbolTrackerImpl{host: host, resolver: resolver, state: state, getIsolatedDeclarationError: createGetIsolatedDeclarationErrors(resolver)}
return tracker
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
package declarations
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
)
func needsScopeMarker(result *ast.Node) bool {
return !ast.IsAnyImportOrReExport(result) && !ast.IsExportAssignment(result) && !ast.HasSyntacticModifier(result, ast.ModifierFlagsExport) && !ast.IsAmbientModule(result)
}
func canHaveLiteralInitializer(host DeclarationEmitHost, node *ast.Node) bool {
switch node.Kind {
case ast.KindPropertyDeclaration,
ast.KindPropertySignature:
return host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) == 0
case ast.KindParameter,
ast.KindVariableDeclaration:
return true
}
return false
}
func canProduceDiagnostics(node *ast.Node) bool {
return ast.IsVariableDeclaration(node) ||
ast.IsPropertyDeclaration(node) ||
ast.IsPropertySignatureDeclaration(node) ||
ast.IsBindingElement(node) ||
ast.IsSetAccessorDeclaration(node) ||
ast.IsGetAccessorDeclaration(node) ||
ast.IsConstructSignatureDeclaration(node) ||
ast.IsCallSignatureDeclaration(node) ||
ast.IsMethodDeclaration(node) ||
ast.IsMethodSignatureDeclaration(node) ||
ast.IsFunctionDeclaration(node) ||
ast.IsParameterDeclaration(node) ||
ast.IsTypeParameterDeclaration(node) ||
ast.IsExpressionWithTypeArguments(node) ||
ast.IsImportEqualsDeclaration(node) ||
ast.IsTypeAliasDeclaration(node) ||
ast.IsJSTypeAliasDeclaration(node) ||
ast.IsConstructorDeclaration(node) ||
ast.IsIndexSignatureDeclaration(node) ||
ast.IsPropertyAccessExpression(node) ||
ast.IsElementAccessExpression(node) ||
ast.IsBinaryExpression(node) ||
ast.IsCallExpression(node) // || // !!! TODO: JSDoc support
/* ast.IsJSDocTypeAlias(node); */
}
func canReuseModifierNodes(nodes []*ast.Node) bool {
for _, node := range nodes {
if ast.IsModifier(node) && node.Flags&ast.NodeFlagsReparsed != 0 {
return false
}
}
return true
}
func isDeclarationAndNotVisible(emitContext *printer.EmitContext, resolver printer.EmitResolver, node *ast.Node) bool {
node = emitContext.ParseNode(node)
switch node.Kind {
case ast.KindFunctionDeclaration,
ast.KindModuleDeclaration,
ast.KindInterfaceDeclaration,
ast.KindClassDeclaration,
ast.KindTypeAliasDeclaration,
ast.KindJSTypeAliasDeclaration,
ast.KindEnumDeclaration:
return !resolver.IsDeclarationVisible(node)
// The following should be doing their own visibility checks based on filtering their members
case ast.KindVariableDeclaration:
return !getBindingNameVisible(resolver, node)
case ast.KindImportEqualsDeclaration,
ast.KindImportDeclaration,
ast.KindJSImportDeclaration,
ast.KindExportDeclaration,
ast.KindExportAssignment:
return false
case ast.KindClassStaticBlockDeclaration:
return true
}
return false
}
func getBindingNameVisible(resolver printer.EmitResolver, elem *ast.Node) bool {
if ast.IsOmittedExpression(elem) {
return false
}
// TODO: parseArrayBindingElement _never_ parses out an OmittedExpression anymore, instead producing a nameless binding element
// Audit if OmittedExpression should be removed
if elem.Name() == nil {
return false
}
if ast.IsBindingPattern(elem.Name()) {
// If any child binding pattern element has been marked visible (usually by collect linked aliases), then this is visible
for _, elem := range elem.Name().Elements() {
if getBindingNameVisible(resolver, elem) {
return true
}
}
return false
} else {
return resolver.IsDeclarationVisible(elem)
}
}
func isEnclosingDeclaration(node *ast.Node) bool {
return ast.IsSourceFile(node) ||
ast.IsTypeAliasDeclaration(node) ||
ast.IsJSTypeAliasDeclaration(node) ||
ast.IsModuleDeclaration(node) ||
ast.IsClassDeclaration(node) ||
ast.IsInterfaceDeclaration(node) ||
ast.IsFunctionLike(node) ||
ast.IsIndexSignatureDeclaration(node) ||
ast.IsMappedTypeNode(node) ||
ast.IsVariableDeclaration(node)
}
func isAlwaysType(node *ast.Node) bool {
if node.Kind == ast.KindInterfaceDeclaration {
return true
}
return false
}
func maskModifierFlags(node *ast.Node, modifierMask ast.ModifierFlags, modifierAdditions ast.ModifierFlags) ast.ModifierFlags {
flags := (ast.GetCombinedModifierFlags(node) & modifierMask) | modifierAdditions
if flags&ast.ModifierFlagsDefault != 0 && (flags&ast.ModifierFlagsExport == 0) {
// A non-exported default is a nonsequitor - we usually try to remove all export modifiers
// from statements in ambient declarations; but a default export must retain its export modifier to be syntactically valid
flags ^= ast.ModifierFlagsExport
}
if flags&ast.ModifierFlagsDefault != 0 && flags&ast.ModifierFlagsAmbient != 0 {
flags ^= ast.ModifierFlagsAmbient // `declare` is never required alongside `default` (and would be an error if printed)
}
return flags
}
func unwrapParenthesizedExpression(o *ast.Node) *ast.Node {
for o.Kind == ast.KindParenthesizedExpression {
o = o.Expression()
}
return o
}
func isPrivateMethodTypeParameter(host DeclarationEmitHost, node *ast.TypeParameterDeclaration) bool {
return node.AsNode().Parent.Kind == ast.KindMethodDeclaration && host.GetEffectiveDeclarationFlags(node.AsNode().Parent, ast.ModifierFlagsPrivate) != 0
}
// Returns true if expando properties should be emitted for this function.
// Properties are emitted if any overload in the symbol has a body (implementation).
func shouldEmitFunctionProperties(input *ast.FunctionDeclaration) bool {
if input.Body != nil {
return true
}
return !core.Every(input.Symbol.Declarations, func(decl *ast.Node) bool {
return !ast.IsFunctionDeclaration(decl) || decl.AsFunctionDeclaration().Body == nil
})
}
func getEffectiveBaseTypeNode(node *ast.Node) *ast.Node {
baseType := ast.GetClassExtendsHeritageElement(node)
// !!! TODO: JSDoc support
// if (baseType && isInJSFile(node)) {
// // Prefer an @augments tag because it may have type parameters.
// const tag = getJSDocAugmentsTag(node);
// if (tag) {
// return tag.class;
// }
// }
return baseType
}
func isScopeMarker(node *ast.Node) bool {
return ast.IsExportAssignment(node) || ast.IsExportDeclaration(node)
}
func hasScopeMarker(statements *ast.StatementList) bool {
if statements == nil {
return false
}
return core.Some(statements.Nodes, isScopeMarker)
}

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)
}

View File

@@ -0,0 +1,995 @@
package estransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type asyncContextFlags int
const (
asyncContextNonTopLevel asyncContextFlags = 1 << iota
asyncContextHasLexicalThis
)
type lexicalArgumentsInfo struct {
binding *ast.IdentifierNode
used bool
}
type asyncTransformer struct {
transformers.Transformer
superAccessState
contextFlags asyncContextFlags
enclosingFunctionParameterNames *collections.Set[string]
lexicalArguments lexicalArgumentsInfo
asyncBodyVisitor *ast.NodeVisitor
fallbackNodeVisitor *ast.NodeVisitor
}
func newAsyncTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &asyncTransformer{}
result := tx.NewTransformer(tx.visit, opts.Context)
tx.initSuperAccessVisitor(tx.EmitContext(), tx.Factory())
tx.asyncBodyVisitor = tx.EmitContext().NewNodeVisitor(tx.visitAsyncBodyNode)
tx.fallbackNodeVisitor = tx.EmitContext().NewNodeVisitor(tx.visitFallback)
return result
}
func (tx *asyncTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
tx.setContextFlag(asyncContextNonTopLevel, false)
tx.setContextFlag(asyncContextHasLexicalThis, false)
visited := tx.Visitor().VisitEachChild(node.AsNode())
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
return visited
}
func (tx *asyncTransformer) setContextFlag(flag asyncContextFlags, val bool) {
if val {
tx.contextFlags |= flag
} else {
tx.contextFlags &^= flag
}
}
func (tx *asyncTransformer) inContext(flags asyncContextFlags) bool {
return tx.contextFlags&flags != 0
}
func (tx *asyncTransformer) inTopLevelContext() bool {
return !tx.inContext(asyncContextNonTopLevel)
}
func (tx *asyncTransformer) inHasLexicalThisContext() bool {
return tx.inContext(asyncContextHasLexicalThis)
}
func (tx *asyncTransformer) doWithContext(flags asyncContextFlags, cb func(*asyncTransformer, *ast.Node) *ast.Node, node *ast.Node) *ast.Node {
flagsToSet := flags & ^tx.contextFlags
if flagsToSet != 0 {
tx.setContextFlag(flagsToSet, true)
result := cb(tx, node)
tx.setContextFlag(flagsToSet, false)
return result
}
return cb(tx, node)
}
func (tx *asyncTransformer) visitDefault(node *ast.Node) *ast.Node {
return tx.Visitor().VisitEachChild(node)
}
func (tx *asyncTransformer) fallbackVisitor(node *ast.Node) *ast.Node {
if tx.capturedSuperProperties == nil && tx.lexicalArguments.binding == nil {
return node
}
tx.trackSuperAccess(node)
switch node.Kind {
case ast.KindFunctionExpression,
ast.KindFunctionDeclaration,
ast.KindMethodDeclaration,
ast.KindGetAccessor,
ast.KindSetAccessor,
ast.KindConstructor:
return node
case ast.KindParameter,
ast.KindBindingElement,
ast.KindVariableDeclaration:
// fall through to visitEachChild
case ast.KindIdentifier:
if tx.lexicalArguments.binding != nil &&
node.Text() == "arguments" &&
!ast.IsIdentifierName(node) &&
!ast.IsLabelName(node) {
tx.lexicalArguments.used = true
return tx.lexicalArguments.binding
}
}
return tx.fallbackNodeVisitor.VisitEachChild(node)
}
func (tx *asyncTransformer) visitFallback(node *ast.Node) *ast.Node {
return tx.fallbackVisitor(node)
}
func (tx *asyncTransformer) visit(node *ast.Node) *ast.Node {
if tx.EmitContext().EmitFlags(node)&printer.EFNoLexicalThis != 0 && tx.inHasLexicalThisContext() {
tx.setContextFlag(asyncContextHasLexicalThis, false)
defer tx.setContextFlag(asyncContextHasLexicalThis, true)
}
if node.SubtreeFacts()&(ast.SubtreeContainsAnyAwait|ast.SubtreeContainsAwait) == 0 {
return tx.fallbackVisitor(node)
}
tx.trackSuperAccess(node)
switch node.Kind {
case ast.KindAsyncKeyword:
// ES2017 async modifier should be elided for targets < ES2017
return nil
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
case ast.KindAwaitExpression:
return tx.visitAwaitExpression(node.AsAwaitExpression())
case ast.KindMethodDeclaration:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitMethodDeclaration, node)
case ast.KindFunctionDeclaration:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitFunctionDeclaration, node)
case ast.KindFunctionExpression:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitFunctionExpression, node)
case ast.KindArrowFunction:
return tx.doWithContext(asyncContextNonTopLevel, (*asyncTransformer).visitArrowFunction, node)
case ast.KindGetAccessor:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitGetAccessorDeclaration, node)
case ast.KindSetAccessor:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitSetAccessorDeclaration, node)
case ast.KindConstructor:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitConstructorDeclaration, node)
case ast.KindClassDeclaration, ast.KindClassExpression:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitDefault, node)
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *asyncTransformer) visitAsyncBodyNode(node *ast.Node) *ast.Node {
if isNodeWithPossibleHoistedDeclaration(node) {
switch node.Kind {
case ast.KindVariableStatement:
return tx.visitVariableStatementInAsyncBody(node)
case ast.KindForStatement:
return tx.visitForStatementInAsyncBody(node.AsForStatement())
case ast.KindForInStatement:
return tx.visitForInStatementInAsyncBody(node.AsForInOrOfStatement())
case ast.KindForOfStatement:
return tx.visitForOfStatementInAsyncBody(node.AsForInOrOfStatement())
case ast.KindCatchClause:
return tx.visitCatchClauseInAsyncBody(node.AsCatchClause())
case ast.KindBlock,
ast.KindSwitchStatement,
ast.KindCaseBlock,
ast.KindCaseClause,
ast.KindDefaultClause,
ast.KindTryStatement,
ast.KindDoStatement,
ast.KindWhileStatement,
ast.KindIfStatement,
ast.KindWithStatement,
ast.KindLabeledStatement:
return tx.asyncBodyVisitor.VisitEachChild(node)
}
}
return tx.visit(node)
}
func (tx *asyncTransformer) visitCatchClauseInAsyncBody(node *ast.CatchClause) *ast.Node {
catchClauseNames := &collections.Set[string]{}
if node.VariableDeclaration != nil {
tx.recordDeclarationName(node.VariableDeclaration, catchClauseNames)
}
// names declared in a catch variable are block scoped
var catchClauseUnshadowedNames *collections.Set[string]
for escapedName := range catchClauseNames.Keys() {
if tx.enclosingFunctionParameterNames != nil && tx.enclosingFunctionParameterNames.Has(escapedName) {
if catchClauseUnshadowedNames == nil {
catchClauseUnshadowedNames = tx.enclosingFunctionParameterNames.Clone()
}
catchClauseUnshadowedNames.Delete(escapedName)
}
}
if catchClauseUnshadowedNames != nil {
savedEnclosingFunctionParameterNames := tx.enclosingFunctionParameterNames
tx.enclosingFunctionParameterNames = catchClauseUnshadowedNames
result := tx.asyncBodyVisitor.VisitEachChild(node.AsNode())
tx.enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames
return result
}
return tx.asyncBodyVisitor.VisitEachChild(node.AsNode())
}
func (tx *asyncTransformer) visitVariableStatementInAsyncBody(node *ast.Node) *ast.Node {
declList := node.AsVariableStatement().DeclarationList
if tx.isVariableDeclarationListWithCollidingName(declList) {
expression := tx.visitVariableDeclarationListWithCollidingNames(declList.AsVariableDeclarationList(), false)
if expression != nil {
return tx.Factory().NewExpressionStatement(expression)
}
return nil
}
return tx.Visitor().VisitEachChild(node)
}
func (tx *asyncTransformer) visitForInStatementInAsyncBody(node *ast.ForInOrOfStatement) *ast.Node {
var visitedInitializer *ast.Node
if tx.isVariableDeclarationListWithCollidingName(node.Initializer) {
visitedInitializer = tx.visitVariableDeclarationListWithCollidingNames(node.Initializer.AsVariableDeclarationList(), true)
} else {
visitedInitializer = tx.Visitor().VisitNode(node.Initializer)
}
return tx.Factory().UpdateForInOrOfStatement(
node,
nil, /*awaitModifier*/
visitedInitializer,
tx.Visitor().VisitNode(node.Expression),
tx.asyncBodyVisitor.VisitEmbeddedStatement(node.Statement),
)
}
func (tx *asyncTransformer) visitForOfStatementInAsyncBody(node *ast.ForInOrOfStatement) *ast.Node {
var visitedInitializer *ast.Node
if tx.isVariableDeclarationListWithCollidingName(node.Initializer) {
visitedInitializer = tx.visitVariableDeclarationListWithCollidingNames(node.Initializer.AsVariableDeclarationList(), true)
} else {
visitedInitializer = tx.Visitor().VisitNode(node.Initializer)
}
return tx.Factory().UpdateForInOrOfStatement(
node,
tx.Visitor().VisitNode(node.AwaitModifier),
visitedInitializer,
tx.Visitor().VisitNode(node.Expression),
tx.asyncBodyVisitor.VisitEmbeddedStatement(node.Statement),
)
}
func (tx *asyncTransformer) visitForStatementInAsyncBody(node *ast.ForStatement) *ast.Node {
initializer := node.Initializer
var visitedInitializer *ast.Node
if initializer != nil && tx.isVariableDeclarationListWithCollidingName(initializer) {
visitedInitializer = tx.visitVariableDeclarationListWithCollidingNames(initializer.AsVariableDeclarationList(), false)
} else {
visitedInitializer = tx.Visitor().VisitNode(node.Initializer)
}
return tx.Factory().UpdateForStatement(
node,
visitedInitializer,
tx.Visitor().VisitNode(node.Condition),
tx.Visitor().VisitNode(node.Incrementor),
tx.asyncBodyVisitor.VisitEmbeddedStatement(node.Statement),
)
}
// visitAwaitExpression visits an AwaitExpression node.
//
// This function will be called any time a ES2017 await expression is encountered.
func (tx *asyncTransformer) visitAwaitExpression(node *ast.AwaitExpression) *ast.Node {
// do not downlevel a top-level await as it is module syntax...
if tx.inTopLevelContext() {
return tx.Visitor().VisitEachChild(node.AsNode())
}
yieldExpr := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.Visitor().VisitNode(node.Expression),
)
yieldExpr.Loc = node.Loc
tx.EmitContext().SetOriginal(yieldExpr, node.AsNode())
return yieldExpr
}
func (tx *asyncTransformer) visitConstructorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsConstructorDeclaration()
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
updated := tx.Factory().UpdateConstructorDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.transformMethodBody(node),
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitMethodDeclaration visits a MethodDeclaration node.
//
// This function will be called when one of the following conditions are met:
// - The node is marked as async
func (tx *asyncTransformer) visitMethodDeclaration(node *ast.Node) *ast.Node {
decl := node.AsMethodDeclaration()
functionFlags := ast.GetFunctionFlags(node)
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.transformMethodBody(node)
}
updated := tx.Factory().UpdateMethodDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.AsteriskToken,
decl.Name(),
nil, /*postfixToken*/
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
func (tx *asyncTransformer) visitGetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsGetAccessorDeclaration()
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
updated := tx.Factory().UpdateGetAccessorDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.Name(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.transformMethodBody(node),
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
func (tx *asyncTransformer) visitSetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsSetAccessorDeclaration()
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
updated := tx.Factory().UpdateSetAccessorDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.Name(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.transformMethodBody(node),
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitFunctionDeclaration visits a FunctionDeclaration node.
//
// This function will be called when one of the following conditions are met:
// - The node is marked async
func (tx *asyncTransformer) visitFunctionDeclaration(node *ast.Node) *ast.Node {
decl := node.AsFunctionDeclaration()
functionFlags := ast.GetFunctionFlags(node)
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(decl.Body, tx.Visitor())
}
updated := tx.Factory().UpdateFunctionDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.AsteriskToken,
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitFunctionExpression visits a FunctionExpression node.
//
// This function will be called when one of the following conditions are met:
// - The node is marked async
func (tx *asyncTransformer) visitFunctionExpression(node *ast.Node) *ast.Node {
decl := node.AsFunctionExpression()
functionFlags := ast.GetFunctionFlags(node)
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(decl.Body, tx.Visitor())
}
updated := tx.Factory().UpdateFunctionExpression(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.AsteriskToken,
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitArrowFunction visits an ArrowFunction.
//
// This function will be called when one of the following conditions are met:
// - The node is marked async
func (tx *asyncTransformer) visitArrowFunction(node *ast.Node) *ast.Node {
// `arguments` in class static blocks is always an error, but we preserve Strada's emit
// behavior for baseline compatibility. In Strada, checker-based `isArgumentsLocalBinding`
// returns false for `arguments` in static blocks (since the binding doesn't exist due to
// the error), so the async transform leaves them untouched.
if tx.EmitContext().EmitFlags(node)&printer.EFNoLexicalArguments != 0 {
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
defer func() { tx.lexicalArguments = savedLexicalArguments }()
}
decl := node.AsArrowFunction()
functionFlags := ast.GetFunctionFlags(node)
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(decl.Body, tx.Visitor())
}
return tx.Factory().UpdateArrowFunction(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
decl.EqualsGreaterThanToken,
body,
)
}
func (tx *asyncTransformer) recordDeclarationName(node *ast.Node, names *collections.Set[string]) {
name := node.Name()
if name == nil {
return
}
if ast.IsIdentifier(name) {
names.Add(name.Text())
} else if ast.IsBindingPattern(name) {
for _, element := range name.AsBindingPattern().Elements.Nodes {
if !ast.IsOmittedExpression(element) {
tx.recordDeclarationName(element, names)
}
}
}
}
func (tx *asyncTransformer) isVariableDeclarationListWithCollidingName(node *ast.Node) bool {
return node != nil &&
ast.IsVariableDeclarationList(node) &&
node.Flags&ast.NodeFlagsBlockScoped == 0 &&
slices.ContainsFunc(node.AsVariableDeclarationList().Declarations.Nodes, tx.collidesWithParameterName)
}
func (tx *asyncTransformer) visitVariableDeclarationListWithCollidingNames(node *ast.VariableDeclarationList, hasReceiver bool) *ast.Node {
tx.hoistVariableDeclarationList(node)
var variables []*ast.Node
for _, decl := range node.Declarations.Nodes {
if decl.AsVariableDeclaration().Initializer != nil {
variables = append(variables, decl)
}
}
if len(variables) == 0 {
if hasReceiver {
name := node.Declarations.Nodes[0].Name()
var target *ast.Node
if ast.IsBindingPattern(name) {
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), name.AsBindingPattern())
} else {
target = name
}
return tx.Visitor().VisitNode(target)
}
return nil
}
var expressions []*ast.Node
for _, variable := range variables {
expressions = append(expressions, tx.transformInitializedVariable(variable.AsVariableDeclaration()))
}
return tx.Factory().InlineExpressions(expressions)
}
func (tx *asyncTransformer) hoistVariableDeclarationList(node *ast.VariableDeclarationList) {
for _, decl := range node.Declarations.Nodes {
tx.hoistVariable(decl)
}
}
func (tx *asyncTransformer) hoistVariable(node *ast.Node) {
name := node.Name()
if name == nil {
return
}
if ast.IsIdentifier(name) {
tx.EmitContext().AddVariableDeclaration(name)
} else if ast.IsBindingPattern(name) {
for _, element := range name.AsBindingPattern().Elements.Nodes {
if !ast.IsOmittedExpression(element) {
tx.hoistVariable(element)
}
}
}
}
func (tx *asyncTransformer) transformInitializedVariable(node *ast.VariableDeclaration) *ast.Node {
var target *ast.Node
if ast.IsBindingPattern(node.Name()) {
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), node.Name().AsBindingPattern())
} else {
target = node.Name()
}
converted := tx.Factory().NewAssignmentExpression(target, node.Initializer)
tx.EmitContext().SetSourceMapRange(converted, node.Loc)
return tx.Visitor().VisitNode(converted)
}
func (tx *asyncTransformer) collidesWithParameterName(node *ast.Node) bool {
name := node.Name()
if name == nil {
return false
}
if ast.IsIdentifier(name) {
return tx.enclosingFunctionParameterNames != nil && tx.enclosingFunctionParameterNames.Has(name.Text())
}
if ast.IsBindingPattern(name) {
for _, element := range name.AsBindingPattern().Elements.Nodes {
if !ast.IsOmittedExpression(element) && tx.collidesWithParameterName(element) {
return true
}
}
}
return false
}
func (tx *asyncTransformer) transformMethodBody(node *ast.Node) *ast.Node {
savedCapturedSuperProperties := tx.capturedSuperProperties
savedHasSuperElementAccess := tx.hasSuperElementAccess
savedHasSuperPropertyAssignment := tx.hasSuperPropertyAssignment
savedSuperBinding := tx.superBinding
savedSuperIndexBinding := tx.superIndexBinding
tx.capturedSuperProperties = &collections.OrderedSet[string]{}
tx.hasSuperElementAccess = false
tx.hasSuperPropertyAssignment = false
tx.superBinding = tx.Factory().NewUniqueNameEx("_super", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.superIndexBinding = tx.Factory().NewUniqueNameEx("_superIndex", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.EmitContext().StartVariableEnvironment()
updated := tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
emitSuperHelpers := (tx.capturedSuperProperties.Size() > 0 || tx.hasSuperElementAccess) &&
(ast.GetFunctionFlags(tx.getOriginalIfFunctionLike(node))&ast.FunctionFlagsAsyncGenerator) != ast.FunctionFlagsAsyncGenerator
if emitSuperHelpers {
if tx.capturedSuperProperties.Size() > 0 {
tx.EmitContext().AddInitializationStatement(tx.createSuperAccessVariableStatement())
}
}
mergedStatements := tx.EmitContext().EndAndMergeVariableEnvironmentList(updated.StatementList())
if emitSuperHelpers && tx.hasSuperElementAccess && !updated.AsBlock().MultiLine {
newBlock := tx.Factory().NewBlock(mergedStatements, true)
newBlock.Loc = updated.Loc
updated = newBlock
} else {
updated = tx.Factory().UpdateBlock(updated.AsBlock(), mergedStatements, updated.AsBlock().MultiLine)
}
if emitSuperHelpers && tx.hasSuperElementAccess {
if tx.hasSuperPropertyAssignment {
tx.EmitContext().AddEmitHelper(updated, printer.AdvancedAsyncSuperHelper)
} else {
tx.EmitContext().AddEmitHelper(updated, printer.AsyncSuperHelper)
}
}
tx.capturedSuperProperties = savedCapturedSuperProperties
tx.hasSuperElementAccess = savedHasSuperElementAccess
tx.hasSuperPropertyAssignment = savedHasSuperPropertyAssignment
tx.superBinding = savedSuperBinding
tx.superIndexBinding = savedSuperIndexBinding
return updated
}
func (tx *asyncTransformer) createCaptureArgumentsStatement() *ast.Node {
variable := tx.Factory().NewVariableDeclaration(
tx.lexicalArguments.binding,
nil,
nil,
tx.Factory().NewIdentifier("arguments"),
)
declList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{variable}), ast.NodeFlagsNone)
statement := tx.Factory().NewVariableStatement(nil, declList)
tx.EmitContext().AddEmitFlags(statement, printer.EFStartOnNewLine|printer.EFCustomPrologue)
return statement
}
func (tx *asyncTransformer) transformAsyncFunctionParameterList(node *ast.Node) *ast.NodeList {
if isSimpleParameterList(node.Parameters()) {
return tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
var newParameters []*ast.Node
for _, parameter := range node.Parameters() {
param := parameter.AsParameterDeclaration()
if param.Initializer != nil || param.DotDotDotToken != nil {
// for an arrow function, capture the remaining arguments in a rest parameter.
// for any other function/method this isn't necessary as we can just use `arguments`.
if node.Kind == ast.KindArrowFunction {
restParameter := tx.Factory().NewParameterDeclaration(
nil,
tx.Factory().NewToken(ast.KindDotDotDotToken),
tx.Factory().NewUniqueNameEx("args", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}),
nil,
nil,
nil,
)
newParameters = append(newParameters, restParameter)
}
break
}
// for arrow functions we capture fixed parameters to forward to `__awaiter`. For all other functions
// we add fixed parameters to preserve the function's `length` property.
newParameter := tx.Factory().NewParameterDeclaration(
nil,
nil,
tx.Factory().NewGeneratedNameForNodeEx(param.Name(), printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}),
nil,
nil,
nil,
)
newParameters = append(newParameters, newParameter)
}
newParametersArray := tx.Factory().NewNodeList(newParameters)
newParametersArray.Loc = node.ParameterList().Loc
return newParametersArray
}
func (tx *asyncTransformer) transformAsyncFunctionBody(node *ast.Node, outerParameters *ast.NodeList) *ast.Node {
isArrow := node.Kind == ast.KindArrowFunction
savedCapturedSuperProperties := tx.capturedSuperProperties
savedHasSuperElementAccess := tx.hasSuperElementAccess
savedHasSuperPropertyAssignment := tx.hasSuperPropertyAssignment
savedSuperBinding := tx.superBinding
savedSuperIndexBinding := tx.superIndexBinding
if !isArrow {
tx.capturedSuperProperties = &collections.OrderedSet[string]{}
tx.hasSuperElementAccess = false
tx.hasSuperPropertyAssignment = false
tx.superBinding = tx.Factory().NewUniqueNameEx("_super", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.superIndexBinding = tx.Factory().NewUniqueNameEx("_superIndex", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
}
innerParameters := (*ast.NodeList)(nil)
if !isSimpleParameterList(node.Parameters()) {
innerParameters = tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
savedLexicalArguments := tx.lexicalArguments
captureLexicalArguments := tx.lexicalArguments.binding == nil
if captureLexicalArguments {
tx.lexicalArguments = lexicalArgumentsInfo{
binding: tx.Factory().NewUniqueName("arguments"),
}
}
var argumentsExpression *ast.Expression
if innerParameters != nil {
if isArrow {
// `node` does not have a simple parameter list, so `outerParameters` refers to placeholders that are
// forwarded to `innerParameters`, matching how they are introduced in `transformAsyncFunctionParameterList`.
var parameterBindings []*ast.Node
outerLen := len(outerParameters.Nodes)
for i, param := range node.Parameters() {
if i >= outerLen {
break
}
originalParameter := param.AsParameterDeclaration()
outerParameter := outerParameters.Nodes[i].AsParameterDeclaration()
if originalParameter.Initializer != nil || originalParameter.DotDotDotToken != nil {
parameterBindings = append(parameterBindings, tx.Factory().NewSpreadElement(outerParameter.Name()))
break
}
parameterBindings = append(parameterBindings, outerParameter.Name())
}
argumentsExpression = tx.Factory().NewArrayLiteralExpression(tx.Factory().NewNodeList(parameterBindings), false)
} else {
argumentsExpression = tx.Factory().NewIdentifier("arguments")
}
}
// An async function is emit as an outer function that calls an inner
// generator function. To preserve lexical bindings, we pass the current
// `this` and `arguments` objects to `__awaiter`. The generator function
// passed to `__awaiter` is executed inside of the callback to the
// promise constructor.
savedEnclosingFunctionParameterNames := tx.enclosingFunctionParameterNames
tx.enclosingFunctionParameterNames = &collections.Set[string]{}
for _, parameter := range node.Parameters() {
tx.recordDeclarationName(parameter, tx.enclosingFunctionParameterNames)
}
hasLexicalThis := tx.inHasLexicalThisContext()
asyncBody := tx.transformAsyncFunctionBodyWorker(node.Body())
asyncBody = tx.Factory().UpdateBlock(
asyncBody.AsBlock(),
tx.EmitContext().EndAndMergeVariableEnvironmentList(asyncBody.StatementList()),
asyncBody.AsBlock().MultiLine,
)
// Substitute super property accesses with _super/_superIndex helpers
emitSuperHelpers := tx.capturedSuperProperties != nil &&
(tx.capturedSuperProperties.Size() > 0 || tx.hasSuperElementAccess)
if emitSuperHelpers {
innerParameters = tx.superAccessVisitor.VisitNodes(innerParameters)
asyncBody = tx.substituteSuperAccessesInBody(asyncBody)
}
var result *ast.Node
if !isArrow {
tx.EmitContext().StartVariableEnvironment()
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
if emitSuperHelpers {
if tx.capturedSuperProperties.Size() > 0 {
tx.EmitContext().AddInitializationStatement(tx.createSuperAccessVariableStatement())
}
}
if captureLexicalArguments && tx.lexicalArguments.used {
tx.EmitContext().AddInitializationStatement(tx.createCaptureArgumentsStatement())
}
statements := []*ast.Node{
tx.Factory().NewReturnStatement(
tx.Factory().NewAwaiterHelper(
hasLexicalThis,
argumentsExpression,
innerParameters,
asyncBody,
),
),
}
block := tx.Factory().NewBlock(
tx.EmitContext().EndAndMergeVariableEnvironmentList(tx.Factory().NewNodeList(statements)),
true,
)
block.Loc = node.Body().Loc
if emitSuperHelpers && tx.hasSuperElementAccess {
if tx.hasSuperPropertyAssignment {
tx.EmitContext().AddEmitHelper(block, printer.AdvancedAsyncSuperHelper)
} else {
tx.EmitContext().AddEmitHelper(block, printer.AsyncSuperHelper)
}
}
result = block
} else {
result = tx.Factory().NewAwaiterHelper(
hasLexicalThis,
argumentsExpression,
innerParameters,
asyncBody,
)
if captureLexicalArguments && tx.lexicalArguments.used {
block := tx.convertToFunctionBlock(result)
result = tx.Factory().UpdateBlock(
block.AsBlock(),
tx.EmitContext().MergeEnvironmentList(block.StatementList(), []*ast.Node{tx.createCaptureArgumentsStatement()}),
block.AsBlock().MultiLine,
)
}
}
tx.enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames
if !isArrow {
tx.capturedSuperProperties = savedCapturedSuperProperties
tx.hasSuperElementAccess = savedHasSuperElementAccess
tx.hasSuperPropertyAssignment = savedHasSuperPropertyAssignment
tx.superBinding = savedSuperBinding
tx.superIndexBinding = savedSuperIndexBinding
tx.lexicalArguments = savedLexicalArguments
} else if captureLexicalArguments && !tx.lexicalArguments.used {
// If we created a new binding but it wasn't used, restore the previous state.
// If it was used, keep the binding alive so sibling arrows can reuse it
// (the `var` declaration hoists to the enclosing function scope).
tx.lexicalArguments = savedLexicalArguments
} else if captureLexicalArguments {
// Keep the binding but clear the used flag so siblings don't re-emit the capture statement.
tx.lexicalArguments.used = false
}
return result
}
func (tx *asyncTransformer) transformAsyncFunctionBodyWorker(body *ast.Node) *ast.Node {
if ast.IsBlock(body) {
return tx.Factory().UpdateBlock(
body.AsBlock(),
tx.asyncBodyVisitor.VisitNodes(body.StatementList()),
body.AsBlock().MultiLine,
)
}
// Convert expression body to block body with return statement
visited := tx.asyncBodyVisitor.VisitNode(body)
ret := tx.Factory().NewReturnStatement(visited)
ret.Loc = body.Loc
list := tx.Factory().NewNodeList([]*ast.Node{ret})
list.Loc = body.Loc
block := tx.Factory().NewBlock(list, false /*multiLine*/)
block.Loc = body.Loc
return block
}
func (tx *asyncTransformer) convertToFunctionBlock(node *ast.Node) *ast.Node {
if ast.IsBlock(node) {
return node
}
ret := tx.Factory().NewReturnStatement(node)
ret.Loc = node.Loc
tx.EmitContext().SetOriginal(ret, node)
list := tx.Factory().NewNodeList([]*ast.Node{ret})
list.Loc = node.Loc
block := tx.Factory().NewBlock(list, true)
block.Loc = node.Loc
return block
}
// assignmentTargetContainsSuperProperty checks top-down whether an assignment target
// expression contains a super property or element access (super.x or super[x]).
// This avoids relying on parent pointers (IsAssignmentTarget) which may not be set
// on synthesized AST nodes from prior transforms.
func assignmentTargetContainsSuperProperty(node *ast.Node) bool {
switch node.Kind {
case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
return node.Expression().Kind == ast.KindSuperKeyword
case ast.KindParenthesizedExpression:
return assignmentTargetContainsSuperProperty(node.AsParenthesizedExpression().Expression)
case ast.KindArrayLiteralExpression:
return slices.ContainsFunc(node.AsArrayLiteralExpression().Elements.Nodes, assignmentTargetContainsSuperProperty)
case ast.KindObjectLiteralExpression:
for _, prop := range node.AsObjectLiteralExpression().Properties.Nodes {
switch prop.Kind {
case ast.KindPropertyAssignment:
if assignmentTargetContainsSuperProperty(prop.AsPropertyAssignment().Initializer) {
return true
}
case ast.KindShorthandPropertyAssignment:
if assignmentTargetContainsSuperProperty(prop.AsShorthandPropertyAssignment().Name()) {
return true
}
case ast.KindSpreadAssignment:
if assignmentTargetContainsSuperProperty(prop.AsSpreadAssignment().Expression) {
return true
}
}
}
case ast.KindSpreadElement:
return assignmentTargetContainsSuperProperty(node.AsSpreadElement().Expression)
}
return false
}
// isUpdateExpression checks if a prefix/postfix unary expression is ++ or --.
func isUpdateExpression(node *ast.Node) bool {
if ast.IsPrefixUnaryExpression(node) {
op := node.AsPrefixUnaryExpression().Operator
return op == ast.KindPlusPlusToken || op == ast.KindMinusMinusToken
}
if ast.IsPostfixUnaryExpression(node) {
op := node.AsPostfixUnaryExpression().Operator
return op == ast.KindPlusPlusToken || op == ast.KindMinusMinusToken
}
return false
}
func (tx *asyncTransformer) getOriginalIfFunctionLike(node *ast.Node) *ast.Node {
original := tx.EmitContext().MostOriginal(node)
if original != nil && ast.IsFunctionLikeDeclaration(original) {
return original
}
return node
}
// isSimpleParameterList checks if every parameter has no initializer and an Identifier name.
func isSimpleParameterList(params []*ast.Node) bool {
for _, param := range params {
p := param.AsParameterDeclaration()
if p.Initializer != nil || !ast.IsIdentifier(p.Name()) {
return false
}
}
return true
}
// isNodeWithPossibleHoistedDeclaration checks if a node could contain hoisted declarations.
func isNodeWithPossibleHoistedDeclaration(node *ast.Node) bool {
switch node.Kind {
case ast.KindBlock,
ast.KindVariableStatement,
ast.KindWithStatement,
ast.KindIfStatement,
ast.KindSwitchStatement,
ast.KindCaseBlock,
ast.KindCaseClause,
ast.KindDefaultClause,
ast.KindLabeledStatement,
ast.KindForStatement,
ast.KindForInStatement,
ast.KindForOfStatement,
ast.KindDoStatement,
ast.KindWhileStatement,
ast.KindTryStatement,
ast.KindCatchClause:
return true
}
return false
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/printer"
)
// Gets whether a node is a `static {}` block containing only a single assignment of the static `this` to the `_classThis`
// (or similar) variable stored in the `classthis` property of the block's `EmitNode`.
func isClassThisAssignmentBlock(emitContext *printer.EmitContext, node *ast.Node) bool {
if ast.IsClassStaticBlockDeclaration(node) {
n := node.AsClassStaticBlockDeclaration()
body := n.Body.AsBlock()
if len(body.Statements.Nodes) == 1 {
statement := body.Statements.Nodes[0]
if ast.IsExpressionStatement(statement) {
expression := statement.Expression()
if ast.IsAssignmentExpression(expression, true /*excludeCompoundAssignment*/) {
binary := expression.AsBinaryExpression()
return ast.IsIdentifier(binary.Left) &&
emitContext.ClassThis(node) == binary.Left &&
binary.Right.Kind == ast.KindThisKeyword
}
}
}
}
return false
}

View File

@@ -0,0 +1,43 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
var (
esDecoratorAndClassFields = transformers.Chain(newESDecoratorTransformer, newClassFieldsTransformer)
NewESNextTransformer = transformers.Chain(newUsingDeclarationTransformer, esDecoratorAndClassFields)
// 2025: only module system syntax (import attributes, json modules), untransformed regex modifiers
// 2024: no new downlevel syntax
// 2023: no new downlevel syntax
// 2022: class static blocks and class fields are handled by newClassFieldsTransformer
NewES2021Transformer = transformers.Chain(NewESNextTransformer, newLogicalAssignmentTransformer)
NewES2020Transformer = transformers.Chain(NewES2021Transformer, newNullishCoalescingTransformer, newOptionalChainTransformer)
NewES2019Transformer = transformers.Chain(NewES2020Transformer, newOptionalCatchTransformer)
NewES2018Transformer = transformers.Chain(NewES2019Transformer, newObjectRestSpreadTransformer, newforawaitTransformer, newTaggedTemplateLiftRestrictionTransformer)
NewES2017Transformer = transformers.Chain(NewES2018Transformer, newAsyncTransformer)
NewES2016Transformer = transformers.Chain(NewES2017Transformer, newExponentiationTransformer)
)
func GetESTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
options := opts.CompilerOptions
switch options.GetEmitScriptTarget() {
case core.ScriptTargetESNext:
return esDecoratorAndClassFields(opts)
case core.ScriptTargetES2025, core.ScriptTargetES2024, core.ScriptTargetES2023, core.ScriptTargetES2022, core.ScriptTargetES2021:
return NewESNextTransformer(opts)
case core.ScriptTargetES2020:
return NewES2021Transformer(opts)
case core.ScriptTargetES2019:
return NewES2020Transformer(opts)
case core.ScriptTargetES2018:
return NewES2019Transformer(opts)
case core.ScriptTargetES2017:
return NewES2018Transformer(opts)
case core.ScriptTargetES2016:
return NewES2017Transformer(opts)
default: // other, older, option, transform maximally
return NewES2016Transformer(opts)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type exponentiationTransformer struct {
transformers.Transformer
}
func (ch *exponentiationTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsExponentiationOperator == 0 {
return node
}
switch node.Kind {
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *exponentiationTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node {
switch node.OperatorToken.Kind {
case ast.KindAsteriskAsteriskEqualsToken:
return ch.visitExponentiationAssignmentExpression(node)
case ast.KindAsteriskAsteriskToken:
return ch.visitExponentiationExpression(node)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *exponentiationTransformer) visitExponentiationAssignmentExpression(node *ast.BinaryExpression) *ast.Node {
var target *ast.Node
var value *ast.Node
left := ch.Visitor().VisitNode(node.Left)
right := ch.Visitor().VisitNode(node.Right)
if ast.IsElementAccessExpression(left) {
// Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)`
expressionTemp := ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(expressionTemp)
argumentExpressionTemp := ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(argumentExpressionTemp)
objExpr := ch.Factory().NewAssignmentExpression(expressionTemp, left.Expression())
objExpr.Loc = left.Expression().Loc
accessExpr := ch.Factory().NewAssignmentExpression(argumentExpressionTemp, left.AsElementAccessExpression().ArgumentExpression)
accessExpr.Loc = left.AsElementAccessExpression().ArgumentExpression.Loc
target = ch.Factory().NewElementAccessExpression(objExpr, nil, accessExpr, ast.NodeFlagsNone)
value = ch.Factory().NewElementAccessExpression(expressionTemp, nil, argumentExpressionTemp, ast.NodeFlagsNone)
value.Loc = left.Loc
} else if ast.IsPropertyAccessExpression(left) {
// Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)`
expressionTemp := ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(expressionTemp)
assignment := ch.Factory().NewAssignmentExpression(expressionTemp, left.Expression())
assignment.Loc = left.Expression().Loc
target = ch.Factory().NewPropertyAccessExpression(assignment, nil, left.Name(), ast.NodeFlagsNone)
target.Loc = left.Loc
value = ch.Factory().NewPropertyAccessExpression(expressionTemp, nil, left.Name(), ast.NodeFlagsNone)
value.Loc = left.Loc
} else {
// Transforms `a **= b` into `a = Math.pow(a, b)`
target = left
value = left
}
rhs := ch.Factory().NewGlobalMethodCall("Math", "pow", []*ast.Node{value, right})
rhs.Loc = node.Loc
result := ch.Factory().NewAssignmentExpression(target, rhs)
result.Loc = node.Loc
return result
}
func (ch *exponentiationTransformer) visitExponentiationExpression(node *ast.BinaryExpression) *ast.Node {
left := ch.Visitor().VisitNode(node.Left)
right := ch.Visitor().VisitNode(node.Right)
result := ch.Factory().NewGlobalMethodCall("Math", "pow", []*ast.Node{left, right})
result.Loc = node.Loc
return result
}
func newExponentiationTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &exponentiationTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,856 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
// Facts we track as we traverse the tree
type forAwaitHierarchyFacts int
const forAwaitHierarchyFactsNone forAwaitHierarchyFacts = 0
const (
//
// Ancestor facts
//
forAwaitHierarchyFactsHasLexicalThis forAwaitHierarchyFacts = 1 << iota
forAwaitHierarchyFactsIterationContainer
//
// Ancestor masks
//
forAwaitHierarchyFactsAncestorFactsMask = 1<<iota - 1
forAwaitHierarchyFactsSourceFileExcludes = forAwaitHierarchyFactsIterationContainer
forAwaitHierarchyFactsStrictModeSourceFileIncludes = forAwaitHierarchyFactsNone
forAwaitHierarchyFactsClassOrFunctionIncludes = forAwaitHierarchyFactsHasLexicalThis
forAwaitHierarchyFactsClassOrFunctionExcludes = forAwaitHierarchyFactsIterationContainer
forAwaitHierarchyFactsArrowFunctionIncludes = forAwaitHierarchyFactsNone
forAwaitHierarchyFactsArrowFunctionExcludes = forAwaitHierarchyFactsClassOrFunctionExcludes
forAwaitHierarchyFactsIterationStatementIncludes = forAwaitHierarchyFactsIterationContainer
forAwaitHierarchyFactsIterationStatementExcludes = forAwaitHierarchyFactsNone
)
type forawaitTransformer struct {
transformers.Transformer
superAccessState
compilerOptions *core.CompilerOptions
enclosingFunctionFlags ast.FunctionFlags
forAwaitHierarchyFacts forAwaitHierarchyFacts
exportedVariableStatement bool
fallbackNodeVisitor *ast.NodeVisitor
noAsyncModifierVisitor *ast.NodeVisitor
}
func newforawaitTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &forawaitTransformer{
compilerOptions: opts.CompilerOptions,
}
result := tx.NewTransformer(tx.visit, opts.Context)
tx.initSuperAccessVisitor(tx.EmitContext(), tx.Factory())
tx.fallbackNodeVisitor = tx.EmitContext().NewNodeVisitor(tx.visitFallback)
tx.noAsyncModifierVisitor = tx.EmitContext().NewNodeVisitor(func(node *ast.Node) *ast.Node {
if node.Kind == ast.KindAsyncKeyword {
return nil
}
return node
})
return result
}
func (tx *forawaitTransformer) affectsSubtree(excludeFacts forAwaitHierarchyFacts, includeFacts forAwaitHierarchyFacts) bool {
return tx.forAwaitHierarchyFacts != (tx.forAwaitHierarchyFacts&^excludeFacts | includeFacts)
}
// enterSubtree sets the HierarchyFacts for this node prior to visiting this node's subtree,
// returning the facts set prior to modification.
func (tx *forawaitTransformer) enterSubtree(excludeFacts forAwaitHierarchyFacts, includeFacts forAwaitHierarchyFacts) forAwaitHierarchyFacts {
ancestorFacts := tx.forAwaitHierarchyFacts
tx.forAwaitHierarchyFacts = (tx.forAwaitHierarchyFacts&^excludeFacts | includeFacts) & forAwaitHierarchyFactsAncestorFactsMask
return ancestorFacts
}
// exitSubtree restores the HierarchyFacts for this node's ancestor after visiting this node's
// subtree.
func (tx *forawaitTransformer) exitSubtree(ancestorFacts forAwaitHierarchyFacts) {
tx.forAwaitHierarchyFacts = ancestorFacts
}
func (tx *forawaitTransformer) visitModifiersNoAsync(modifiers *ast.ModifierList) *ast.ModifierList {
return tx.noAsyncModifierVisitor.VisitModifiers(modifiers)
}
func (tx *forawaitTransformer) doWithHierarchyFacts(cb func(*forawaitTransformer, *ast.Node) *ast.Node, node *ast.Node, excludeFacts forAwaitHierarchyFacts, includeFacts forAwaitHierarchyFacts) *ast.Node {
if tx.affectsSubtree(excludeFacts, includeFacts) {
ancestorFacts := tx.enterSubtree(excludeFacts, includeFacts)
result := cb(tx, node)
tx.exitSubtree(ancestorFacts)
return result
}
return cb(tx, node)
}
func (tx *forawaitTransformer) visitDefault(node *ast.Node) *ast.Node {
return tx.Visitor().VisitEachChild(node)
}
func (tx *forawaitTransformer) fallbackVisitor(node *ast.Node) *ast.Node {
if tx.capturedSuperProperties == nil {
return node
}
switch node.Kind {
case ast.KindFunctionExpression, ast.KindFunctionDeclaration,
ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor,
ast.KindConstructor:
return node
}
tx.trackSuperAccess(node)
return tx.fallbackNodeVisitor.VisitEachChild(node)
}
func (tx *forawaitTransformer) visitFallback(node *ast.Node) *ast.Node {
return tx.fallbackVisitor(node)
}
func (tx *forawaitTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsForAwaitOrAsyncGenerator == 0 {
return tx.fallbackVisitor(node)
}
tx.trackSuperAccess(node)
switch node.Kind {
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
case ast.KindAwaitExpression:
return tx.visitAwaitExpression(node.AsAwaitExpression())
case ast.KindYieldExpression:
return tx.visitYieldExpression(node.AsYieldExpression())
case ast.KindReturnStatement:
return tx.visitReturnStatement(node.AsReturnStatement())
case ast.KindLabeledStatement:
return tx.visitLabeledStatement(node.AsLabeledStatement())
case ast.KindDoStatement, ast.KindWhileStatement, ast.KindForInStatement:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitDefault,
node,
forAwaitHierarchyFactsIterationStatementExcludes,
forAwaitHierarchyFactsIterationStatementIncludes,
)
case ast.KindForOfStatement:
return tx.visitForOfStatement(node.AsForInOrOfStatement(), nil)
case ast.KindForStatement:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitDefault,
node,
forAwaitHierarchyFactsIterationStatementExcludes,
forAwaitHierarchyFactsIterationStatementIncludes,
)
case ast.KindConstructor:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitConstructorDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindMethodDeclaration:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitMethodDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindGetAccessor:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitGetAccessorDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindSetAccessor:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitSetAccessorDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindFunctionDeclaration:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitFunctionDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindFunctionExpression:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitFunctionExpression,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindArrowFunction:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitArrowFunction,
node,
forAwaitHierarchyFactsArrowFunctionExcludes,
forAwaitHierarchyFactsArrowFunctionIncludes,
)
case ast.KindClassDeclaration, ast.KindClassExpression:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitDefault,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *forawaitTransformer) visitAwaitExpression(node *ast.AwaitExpression) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
result := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.Factory().NewAwaitHelper(tx.Visitor().VisitNode(node.Expression)),
)
result.Loc = node.Loc
tx.EmitContext().SetOriginal(result, node.AsNode())
return result
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *forawaitTransformer) visitYieldExpression(node *ast.YieldExpression) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
if node.AsteriskToken != nil {
expression := tx.Visitor().VisitNode(node.Expression)
asyncValuesResult := tx.Factory().NewAsyncValuesHelper(expression)
asyncValuesResult.Loc = expression.Loc
asyncDelegatorResult := tx.Factory().NewAsyncDelegatorHelper(asyncValuesResult)
asyncDelegatorResult.Loc = expression.Loc
innerYield := tx.Factory().UpdateYieldExpression(
node,
node.AsteriskToken,
asyncDelegatorResult,
)
awaitedYield := tx.Factory().NewAwaitHelper(innerYield)
result := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
awaitedYield,
)
result.Loc = node.Loc
tx.EmitContext().SetOriginal(result, node.AsNode())
return result
}
var innerExpression *ast.Node
if node.Expression != nil {
innerExpression = tx.Visitor().VisitNode(node.Expression)
} else {
innerExpression = tx.Factory().NewVoidZeroExpression()
}
result := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.createDownlevelAwait(innerExpression),
)
result.Loc = node.Loc
tx.EmitContext().SetOriginal(result, node.AsNode())
return result
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *forawaitTransformer) visitReturnStatement(node *ast.ReturnStatement) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
var expression *ast.Node
if node.Expression != nil {
expression = tx.Visitor().VisitNode(node.Expression)
} else {
expression = tx.Factory().NewVoidZeroExpression()
}
return tx.Factory().UpdateReturnStatement(
node,
tx.createDownlevelAwait(expression),
)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *forawaitTransformer) visitLabeledStatement(node *ast.LabeledStatement) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
statement := unwrapInnermostStatementOfLabel(node)
if statement.Kind == ast.KindForOfStatement && statement.AsForInOrOfStatement().AwaitModifier != nil {
return tx.visitForOfStatement(statement.AsForInOrOfStatement(), node)
}
return tx.Factory().RestoreEnclosingLabel(tx.Visitor().VisitNode(statement), node)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
// unwrapInnermostStatementOfLabel follows LabeledStatement chains to find the innermost statement.
func unwrapInnermostStatementOfLabel(node *ast.LabeledStatement) *ast.Node {
for {
if node.Statement.Kind != ast.KindLabeledStatement {
return node.Statement
}
node = node.Statement.AsLabeledStatement()
}
}
func (tx *forawaitTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
ancestorFacts := tx.enterSubtree(
forAwaitHierarchyFactsSourceFileExcludes,
forAwaitHierarchyFactsStrictModeSourceFileIncludes,
)
tx.exportedVariableStatement = false
visited := tx.Visitor().VisitEachChild(node.AsNode())
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
tx.exitSubtree(ancestorFacts)
return visited
}
// visitForOfStatement visits a ForOfStatement and converts it into a ES2015-compatible ForOfStatement.
func (tx *forawaitTransformer) visitForOfStatement(node *ast.ForInOrOfStatement, outermostLabeledStatement *ast.LabeledStatement) *ast.Node {
ancestorFacts := tx.enterSubtree(forAwaitHierarchyFactsIterationStatementExcludes, forAwaitHierarchyFactsIterationStatementIncludes)
var result *ast.Node
if node.AwaitModifier != nil {
result = tx.transformForAwaitOfStatement(node, outermostLabeledStatement, ancestorFacts)
} else {
result = tx.Factory().RestoreEnclosingLabel(tx.Visitor().VisitEachChild(node.AsNode()), outermostLabeledStatement)
}
tx.exitSubtree(ancestorFacts)
return result
}
func (tx *forawaitTransformer) convertForOfStatementHead(node *ast.ForInOrOfStatement, boundValue *ast.Node, nonUserCode *ast.Node) *ast.Node {
f := tx.Factory()
value := f.NewTempVariable()
tx.EmitContext().AddVariableDeclaration(value)
iteratorValueExpression := f.NewAssignmentExpression(value, boundValue)
iteratorValueStatement := f.NewExpressionStatement(iteratorValueExpression)
tx.EmitContext().SetSourceMapRange(iteratorValueStatement, node.Expression.Loc)
exitNonUserCodeExpression := f.NewAssignmentExpression(nonUserCode, f.NewKeywordExpression(ast.KindFalseKeyword))
exitNonUserCodeStatement := f.NewExpressionStatement(exitNonUserCodeExpression)
tx.EmitContext().SetSourceMapRange(exitNonUserCodeStatement, node.Expression.Loc)
statements := []*ast.Node{iteratorValueStatement, exitNonUserCodeStatement}
binding := tx.Factory().CreateForOfBindingStatement(node.Initializer, value)
statements = append(statements, tx.Visitor().VisitNode(binding))
var bodyLocation core.TextRange
var statementsLocation core.TextRange
statement := tx.Visitor().VisitEmbeddedStatement(node.Statement)
if ast.IsBlock(statement) {
statements = append(statements, statement.Statements()...)
bodyLocation = statement.Loc
statementsLocation = statement.StatementList().Loc
} else {
statements = append(statements, statement)
}
stmtList := f.NewNodeList(statements)
stmtList.Loc = statementsLocation
block := f.NewBlock(stmtList, true)
block.Loc = bodyLocation
return block
}
func (tx *forawaitTransformer) createDownlevelAwait(expression *ast.Node) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
return tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.Factory().NewAwaitHelper(expression),
)
}
return tx.Factory().NewAwaitExpression(expression)
}
func (tx *forawaitTransformer) transformForAwaitOfStatement(node *ast.ForInOrOfStatement, outermostLabeledStatement *ast.LabeledStatement, ancestorFacts forAwaitHierarchyFacts) *ast.Node {
f := tx.Factory()
expression := tx.Visitor().VisitNode(node.Expression)
var iterator *ast.Node
if ast.IsIdentifier(expression) {
iterator = f.NewGeneratedNameForNode(expression)
} else {
iterator = f.NewTempVariable()
}
var result *ast.Node
if ast.IsIdentifier(expression) {
result = f.NewGeneratedNameForNode(iterator)
} else {
result = f.NewTempVariable()
}
nonUserCode := f.NewTempVariable()
done := f.NewTempVariable()
tx.EmitContext().AddVariableDeclaration(done)
errorRecord := f.NewUniqueName("e")
catchVariable := f.NewGeneratedNameForNode(errorRecord)
returnMethod := f.NewTempVariable()
callValues := f.NewAsyncValuesHelper(expression)
callValues.Loc = node.Expression.Loc
callNext := f.NewCallExpression(
f.NewPropertyAccessExpression(iterator, nil, f.NewIdentifier("next"), ast.NodeFlagsNone),
nil, nil,
f.NewNodeList([]*ast.Node{}),
ast.NodeFlagsNone,
)
getDone := f.NewPropertyAccessExpression(result, nil, f.NewIdentifier("done"), ast.NodeFlagsNone)
getValue := f.NewPropertyAccessExpression(result, nil, f.NewIdentifier("value"), ast.NodeFlagsNone)
callReturn := f.NewFunctionCallCall(returnMethod, iterator, []*ast.Node{})
tx.EmitContext().AddVariableDeclaration(errorRecord)
tx.EmitContext().AddVariableDeclaration(returnMethod)
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
var initializer *ast.Node
if ancestorFacts&forAwaitHierarchyFactsIterationContainer != 0 {
initializer = f.InlineExpressions([]*ast.Node{
f.NewAssignmentExpression(errorRecord, f.NewVoidZeroExpression()),
callValues,
})
} else {
initializer = callValues
}
// Build the for statement
iteratorDecl := f.NewVariableDeclaration(iterator, nil, nil, initializer)
iteratorDecl.Loc = node.Expression.Loc
varDeclList := f.NewVariableDeclarationList(f.NewNodeList([]*ast.Node{
f.NewVariableDeclaration(nonUserCode, nil, nil, f.NewKeywordExpression(ast.KindTrueKeyword)),
iteratorDecl,
f.NewVariableDeclaration(result, nil, nil, nil),
}), ast.NodeFlagsNone)
varDeclList.Loc = node.Expression.Loc
condition := f.InlineExpressions([]*ast.Node{
f.NewAssignmentExpression(result, tx.createDownlevelAwait(callNext)),
f.NewAssignmentExpression(done, getDone),
f.NewPrefixUnaryExpression(ast.KindExclamationToken, done),
})
incrementor := f.NewAssignmentExpression(nonUserCode, f.NewKeywordExpression(ast.KindTrueKeyword))
forStatement := f.NewForStatement(
varDeclList,
condition,
incrementor,
tx.convertForOfStatementHead(node, getValue, nonUserCode),
)
forStatement.Loc = node.Loc
tx.EmitContext().AddEmitFlags(forStatement, printer.EFNoTokenTrailingSourceMaps)
tx.EmitContext().SetOriginal(forStatement, node.AsNode())
// Build the try/catch/finally
tryBlock := f.NewBlock(f.NewNodeList([]*ast.Node{
f.RestoreEnclosingLabel(forStatement, outermostLabeledStatement),
}), true)
// catch clause: { e_1 = { error: e_2 }; }
catchBody := f.NewBlock(f.NewNodeList([]*ast.Node{
f.NewExpressionStatement(
f.NewAssignmentExpression(
errorRecord,
f.NewObjectLiteralExpression(f.NewNodeList([]*ast.Node{
f.NewPropertyAssignment(nil, f.NewIdentifier("error"), nil, nil, catchVariable),
}), false),
),
),
}), false)
tx.EmitContext().AddEmitFlags(catchBody, printer.EFSingleLine)
catchClause := f.NewCatchClause(
f.NewVariableDeclaration(catchVariable, nil, nil, nil),
catchBody,
)
// finally block
// inner try: if (!nonUserCode && !done && (returnMethod = iterator.return)) await returnMethod.call(iterator);
innerIfCondition := f.NewBinaryExpression(
nil,
f.NewBinaryExpression(
nil,
f.NewPrefixUnaryExpression(ast.KindExclamationToken, nonUserCode),
nil,
f.NewToken(ast.KindAmpersandAmpersandToken),
f.NewPrefixUnaryExpression(ast.KindExclamationToken, done),
),
nil,
f.NewToken(ast.KindAmpersandAmpersandToken),
f.NewAssignmentExpression(
returnMethod,
f.NewPropertyAccessExpression(iterator, nil, f.NewIdentifier("return"), ast.NodeFlagsNone),
),
)
innerIfStatement := f.NewIfStatement(
innerIfCondition,
f.NewExpressionStatement(tx.createDownlevelAwait(callReturn)),
nil,
)
tx.EmitContext().AddEmitFlags(innerIfStatement, printer.EFSingleLine)
innerTryBlock := f.NewBlock(f.NewNodeList([]*ast.Node{innerIfStatement}), false)
// inner finally: if (errorRecord) throw errorRecord.error;
innerFinallyIf := f.NewIfStatement(
errorRecord,
f.NewThrowStatement(
f.NewPropertyAccessExpression(errorRecord, nil, f.NewIdentifier("error"), ast.NodeFlagsNone),
),
nil,
)
tx.EmitContext().AddEmitFlags(innerFinallyIf, printer.EFSingleLine)
innerFinallyBlock := f.NewBlock(f.NewNodeList([]*ast.Node{innerFinallyIf}), false)
tx.EmitContext().AddEmitFlags(innerFinallyBlock, printer.EFSingleLine)
innerTryStatement := f.NewTryStatement(innerTryBlock, nil, innerFinallyBlock)
finallyBlock := f.NewBlock(f.NewNodeList([]*ast.Node{innerTryStatement}), true)
return f.NewTryStatement(tryBlock, catchClause, finallyBlock)
}
func (tx *forawaitTransformer) visitConstructorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsConstructorDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateConstructorDeclaration(
decl,
decl.Modifiers(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitGetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsGetAccessorDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateGetAccessorDeclaration(
decl,
decl.Modifiers(),
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitSetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsSetAccessorDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateSetAccessorDeclaration(
decl,
decl.Modifiers(),
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitMethodDeclaration(node *ast.Node) *ast.Node {
decl := node.AsMethodDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
var modifiers *ast.ModifierList
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
modifiers = tx.visitModifiersNoAsync(decl.Modifiers())
} else {
modifiers = decl.Modifiers()
}
var asteriskToken *ast.TokenNode
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
asteriskToken = nil
} else {
asteriskToken = decl.AsteriskToken
}
var parameters *ast.NodeList
var body *ast.Node
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
parameters = tx.transformAsyncGeneratorFunctionParameterList(node)
body = tx.transformAsyncGeneratorFunctionBody(node)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
}
updated := tx.Factory().UpdateMethodDeclaration(
decl,
modifiers,
asteriskToken,
tx.Visitor().VisitNode(decl.Name()),
nil, /*postfixToken*/
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitFunctionDeclaration(node *ast.Node) *ast.Node {
decl := node.AsFunctionDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
var modifiers *ast.ModifierList
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
modifiers = tx.visitModifiersNoAsync(decl.Modifiers())
} else {
modifiers = decl.Modifiers()
}
var asteriskToken *ast.TokenNode
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
asteriskToken = nil
} else {
asteriskToken = decl.AsteriskToken
}
var parameters *ast.NodeList
var body *ast.Node
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
parameters = tx.transformAsyncGeneratorFunctionParameterList(node)
body = tx.transformAsyncGeneratorFunctionBody(node)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
}
updated := tx.Factory().UpdateFunctionDeclaration(
decl,
modifiers,
asteriskToken,
decl.Name(),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitArrowFunction(node *ast.Node) *ast.Node {
decl := node.AsArrowFunction()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateArrowFunction(
decl,
decl.Modifiers(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
decl.EqualsGreaterThanToken,
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitFunctionExpression(node *ast.Node) *ast.Node {
decl := node.AsFunctionExpression()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
var modifiers *ast.ModifierList
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
modifiers = tx.visitModifiersNoAsync(decl.Modifiers())
} else {
modifiers = decl.Modifiers()
}
var asteriskToken *ast.TokenNode
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
asteriskToken = nil
} else {
asteriskToken = decl.AsteriskToken
}
var parameters *ast.NodeList
var body *ast.Node
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
parameters = tx.transformAsyncGeneratorFunctionParameterList(node)
body = tx.transformAsyncGeneratorFunctionBody(node)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
}
updated := tx.Factory().UpdateFunctionExpression(
decl,
modifiers,
asteriskToken,
decl.Name(),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) transformAsyncGeneratorFunctionParameterList(node *ast.Node) *ast.NodeList {
if isSimpleParameterList(node.Parameters()) {
return tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
// Add fixed parameters to preserve the function's `length` property.
var newParameters []*ast.Node
for _, parameter := range node.Parameters() {
param := parameter.AsParameterDeclaration()
if param.Initializer != nil || param.DotDotDotToken != nil {
break
}
newParameter := tx.Factory().NewParameterDeclaration(
nil,
nil,
tx.Factory().NewGeneratedNameForNodeEx(param.Name(), printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}),
nil,
nil,
nil,
)
newParameters = append(newParameters, newParameter)
}
newParametersArray := tx.Factory().NewNodeList(newParameters)
newParametersArray.Loc = node.ParameterList().Loc
return newParametersArray
}
func (tx *forawaitTransformer) transformAsyncGeneratorFunctionBody(node *ast.Node) *ast.Node {
f := tx.Factory()
var innerParameters *ast.NodeList
if !isSimpleParameterList(node.Parameters()) {
innerParameters = tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
savedCapturedSuperProperties := tx.capturedSuperProperties
savedHasSuperElementAccess := tx.hasSuperElementAccess
savedHasSuperPropertyAssignment := tx.hasSuperPropertyAssignment
savedSuperBinding := tx.superBinding
savedSuperIndexBinding := tx.superIndexBinding
tx.capturedSuperProperties = &collections.OrderedSet[string]{}
tx.hasSuperElementAccess = false
tx.hasSuperPropertyAssignment = false
tx.superBinding = f.NewUniqueNameEx("_super", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.superIndexBinding = f.NewUniqueNameEx("_superIndex", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
asyncBody := f.UpdateBlock(
node.Body().AsBlock(),
tx.Visitor().VisitNodes(node.Body().StatementList()),
node.Body().AsBlock().MultiLine,
)
asyncBody = f.UpdateBlock(
asyncBody.AsBlock(),
tx.EmitContext().EndAndMergeVariableEnvironmentList(asyncBody.StatementList()),
asyncBody.AsBlock().MultiLine,
)
// Substitute super property accesses with _super/_superIndex helpers
emitSuperHelpers := tx.capturedSuperProperties.Size() > 0 || tx.hasSuperElementAccess
if emitSuperHelpers {
asyncBody = tx.substituteSuperAccessesInBody(asyncBody)
}
var innerParams *ast.NodeList
if innerParameters != nil {
innerParams = innerParameters
} else {
innerParams = f.NewNodeList([]*ast.Node{})
}
var name *ast.Node
if node.Name() != nil {
name = f.NewGeneratedNameForNode(node.Name())
}
generatorFunc := f.NewFunctionExpression(
nil, /*modifiers*/
f.NewToken(ast.KindAsteriskToken),
name,
nil, /*typeParameters*/
innerParams,
nil, /*returnType*/
nil, /*fullSignature*/
asyncBody,
)
returnStatement := f.NewReturnStatement(
f.NewAsyncGeneratorHelper(
generatorFunc,
tx.forAwaitHierarchyFacts&forAwaitHierarchyFactsHasLexicalThis != 0,
),
)
tx.EmitContext().StartVariableEnvironment()
if emitSuperHelpers {
if tx.capturedSuperProperties.Size() > 0 {
tx.EmitContext().AddInitializationStatement(tx.createSuperAccessVariableStatement())
}
}
outerStatements := []*ast.Node{returnStatement}
block := f.UpdateBlock(
node.Body().AsBlock(),
tx.EmitContext().EndAndMergeVariableEnvironmentList(f.NewNodeList(outerStatements)),
node.Body().AsBlock().MultiLine,
)
if emitSuperHelpers && tx.hasSuperElementAccess {
if tx.hasSuperPropertyAssignment {
tx.EmitContext().AddEmitHelper(block, printer.AdvancedAsyncSuperHelper)
} else {
tx.EmitContext().AddEmitHelper(block, printer.AsyncSuperHelper)
}
}
tx.capturedSuperProperties = savedCapturedSuperProperties
tx.hasSuperElementAccess = savedHasSuperElementAccess
tx.hasSuperPropertyAssignment = savedHasSuperPropertyAssignment
tx.superBinding = savedSuperBinding
tx.superIndexBinding = savedSuperIndexBinding
return block
}

View File

@@ -0,0 +1,113 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type logicalAssignmentTransformer struct {
transformers.Transformer
}
func (ch *logicalAssignmentTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsLogicalAssignments == 0 {
return node
}
switch node.Kind {
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *logicalAssignmentTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node {
var nonAssignmentOperator ast.Kind
switch node.OperatorToken.Kind {
case ast.KindBarBarEqualsToken:
nonAssignmentOperator = ast.KindBarBarToken
case ast.KindAmpersandAmpersandEqualsToken:
nonAssignmentOperator = ast.KindAmpersandAmpersandToken
case ast.KindQuestionQuestionEqualsToken:
nonAssignmentOperator = ast.KindQuestionQuestionToken
default:
return ch.Visitor().VisitEachChild(node.AsNode())
}
left := ast.SkipParentheses(ch.Visitor().VisitNode(node.Left))
assignmentTarget := left
right := ast.SkipParentheses(ch.Visitor().VisitNode(node.Right))
if ast.IsAccessExpression(left) {
propertyAccessTargetSimpleCopiable := transformers.IsSimpleCopiableExpression(left.Expression())
propertyAccessTarget := left.Expression()
propertyAccessTargetAssignment := left.Expression()
if !propertyAccessTargetSimpleCopiable {
propertyAccessTarget = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(propertyAccessTarget)
propertyAccessTargetAssignment = ch.Factory().NewAssignmentExpression(
propertyAccessTarget,
left.Expression(),
)
}
if ast.IsPropertyAccessExpression(left) {
assignmentTarget = ch.Factory().NewPropertyAccessExpression(
propertyAccessTarget,
nil,
left.Name(),
ast.NodeFlagsNone,
)
left = ch.Factory().NewPropertyAccessExpression(
propertyAccessTargetAssignment,
nil,
left.Name(),
ast.NodeFlagsNone,
)
} else {
elementAccessArgumentSimpleCopiable := transformers.IsSimpleCopiableExpression(left.AsElementAccessExpression().ArgumentExpression)
elementAccessArgument := left.AsElementAccessExpression().ArgumentExpression
argumentExpr := elementAccessArgument
if !elementAccessArgumentSimpleCopiable {
elementAccessArgument = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(elementAccessArgument)
argumentExpr = ch.Factory().NewAssignmentExpression(
elementAccessArgument,
left.AsElementAccessExpression().ArgumentExpression,
)
}
assignmentTarget = ch.Factory().NewElementAccessExpression(
propertyAccessTarget,
nil,
elementAccessArgument,
ast.NodeFlagsNone,
)
left = ch.Factory().NewElementAccessExpression(
propertyAccessTargetAssignment,
nil,
argumentExpr,
ast.NodeFlagsNone,
)
}
}
return ch.Factory().NewBinaryExpression(
nil,
left,
nil,
ch.Factory().NewToken(nonAssignmentOperator),
ch.Factory().NewParenthesizedExpression(
ch.Factory().NewAssignmentExpression(
assignmentTarget,
right,
),
),
)
}
func newLogicalAssignmentTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &logicalAssignmentTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,537 @@
package estransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
)
/**
* Gets whether a node is a `static {}` block containing only a single call to the `__setFunctionName` helper where that
* call's second argument is the value stored in the `assignedName` property of the block's `EmitNode`.
* @internal
*/
func isClassNamedEvaluationHelperBlock(emitContext *printer.EmitContext, node *ast.Node) bool {
if !ast.IsClassStaticBlockDeclaration(node) || len(node.AsClassStaticBlockDeclaration().Body.Statements()) != 1 {
return false
}
statement := node.AsClassStaticBlockDeclaration().Body.Statements()[0]
if ast.IsExpressionStatement(statement) {
expression := statement.Expression()
if emitContext.IsCallToHelper(expression, "__setFunctionName") {
arguments := expression.AsCallExpression().Arguments
return len(arguments.Nodes) >= 2 &&
arguments.Nodes[1] == emitContext.AssignedName(node.AsNode())
}
}
return false
}
/**
* Gets whether a `ClassLikeDeclaration` has a `static {}` block containing only a single call to the
* `__setFunctionName` helper.
* @internal
*/
func classHasExplicitlyAssignedName(emitContext *printer.EmitContext, node *ast.ClassLikeDeclaration) bool {
if assignedName := emitContext.AssignedName(node); assignedName != nil {
for _, member := range node.Members() {
if isClassNamedEvaluationHelperBlock(emitContext, member) {
return true
}
}
}
return false
}
/**
* Gets whether a `ClassLikeDeclaration` has a declared name or contains a `static {}` block containing only a single
* call to the `__setFunctionName` helper.
* @internal
*/
func classHasDeclaredOrExplicitlyAssignedName(emitContext *printer.EmitContext, node *ast.ClassLikeDeclaration) bool {
return node.Name() != nil || classHasExplicitlyAssignedName(emitContext, node)
}
type anonymousFunctionDefinition = ast.Node // ClassExpression | FunctionExpression | ArrowFunction
// Indicates whether an expression is an anonymous function definition.
//
// See https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition
func isAnonymousFunctionDefinition(emitContext *printer.EmitContext, node *ast.Expression, cb func(*anonymousFunctionDefinition) bool) bool {
node = ast.SkipOuterExpressions(node, ast.OEKAll)
switch node.Kind {
case ast.KindClassExpression:
if classHasDeclaredOrExplicitlyAssignedName(emitContext, node) {
return false
}
break
case ast.KindFunctionExpression:
if node.AsFunctionExpression().Name() != nil {
return false
}
break
case ast.KindArrowFunction:
break
default:
return false
}
if cb != nil {
return cb(node)
}
return true
}
func isNamedEvaluation(emitContext *printer.EmitContext, node *ast.Node) bool {
return isNamedEvaluationAnd(emitContext, node, nil)
}
func isNamedEvaluationAnd(emitContext *printer.EmitContext, node *ast.Node, cb func(*anonymousFunctionDefinition) bool) bool {
if !ast.IsNamedEvaluationSource(node) {
return false
}
switch node.Kind {
case ast.KindShorthandPropertyAssignment:
return isAnonymousFunctionDefinition(emitContext, node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer, cb)
case ast.KindPropertyAssignment, ast.KindVariableDeclaration, ast.KindParameter, ast.KindBindingElement, ast.KindPropertyDeclaration:
return isAnonymousFunctionDefinition(emitContext, node.Initializer(), cb)
case ast.KindBinaryExpression:
return isAnonymousFunctionDefinition(emitContext, node.AsBinaryExpression().Right, cb)
case ast.KindExportAssignment:
return isAnonymousFunctionDefinition(emitContext, node.Expression(), cb)
default:
debug.Fail("Unhandled case in isNamedEvaluation")
return false
}
}
// Gets a string literal to use as the assigned name of an anonymous class or function declaration.
func getAssignedNameOfIdentifier(emitContext *printer.EmitContext, name *ast.IdentifierNode, expression *ast.Node /*WrappedExpression<AnonymousFunctionDefinition>*/) *ast.StringLiteralNode {
original := emitContext.MostOriginal(ast.SkipOuterExpressions(expression, ast.OEKAll))
if (ast.IsClassDeclaration(original) || ast.IsFunctionDeclaration(original)) &&
original.Name() == nil && ast.HasSyntacticModifier(original, ast.ModifierFlagsDefault) {
return emitContext.Factory.NewStringLiteral("default", ast.TokenFlagsNone)
}
return emitContext.Factory.NewStringLiteralFromNode(name)
}
func getAssignedNameOfPropertyName(emitContext *printer.EmitContext, name *ast.PropertyName, assignedNameText string) (assignedName *ast.Expression, updatedName *ast.PropertyName) {
factory := emitContext.Factory
if len(assignedNameText) > 0 {
assignedName := factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
return assignedName, name
}
if ast.IsPropertyNameLiteral(name) || ast.IsPrivateIdentifier(name) {
assignedName := factory.NewStringLiteralFromNode(name)
return assignedName, name
}
expression := name.Expression()
if ast.IsPropertyNameLiteral(expression) && !ast.IsIdentifier(expression) {
assignedName := factory.NewStringLiteralFromNode(expression)
return assignedName, name
}
debug.Assert(ast.IsComputedPropertyName(name), "Expected computed property name")
assignedName = factory.NewGeneratedNameForNode(name)
emitContext.AddVariableDeclaration(assignedName)
key := factory.NewPropKeyHelper(expression)
assignment := factory.NewAssignmentExpression(assignedName, key)
updatedName = factory.UpdateComputedPropertyName(name.AsComputedPropertyName(), assignment)
return assignedName, updatedName
}
// Creates a class `static {}` block used to dynamically set the name of a class.
//
// The assignedName parameter is the expression used to resolve the assigned name at runtime. This expression should not produce
// side effects.
// The thisExpression parameter overrides the expression to use for the actual `this` reference. This can be used to provide an
// expression that has already had its `EmitFlags` set or may have been tracked to prevent substitution.
func createClassNamedEvaluationHelperBlock(emitContext *printer.EmitContext, assignedName *ast.Expression, thisExpression *ast.Expression) *ast.Node {
// produces:
//
// static { __setFunctionName(this, "C"); }
//
if thisExpression == nil {
thisExpression = emitContext.Factory.NewThisExpression()
}
factory := emitContext.Factory
expression := factory.NewSetFunctionNameHelper(thisExpression, assignedName, "" /*prefix*/)
statement := factory.NewExpressionStatement(expression)
body := factory.NewBlock(factory.NewNodeList([]*ast.Statement{statement}), false /*multiLine*/)
block := factory.NewClassStaticBlockDeclaration(nil /*modifiers*/, body)
// We use `emitNode.assignedName` to indicate this is a NamedEvaluation helper block
// and to stash the expression used to resolve the assigned name.
emitContext.SetAssignedName(block, assignedName)
return block.AsNode()
}
// Injects a class `static {}` block used to dynamically set the name of a class, if one does not already exist.
func injectClassNamedEvaluationHelperBlockIfMissing(
emitContext *printer.EmitContext,
node *ast.ClassLikeDeclaration,
assignedName *ast.Expression,
thisExpression *ast.Expression,
) *ast.ClassLikeDeclaration {
// given:
//
// let C = class {
// };
//
// produces:
//
// let C = class {
// static { __setFunctionName(this, "C"); }
// };
// NOTE: If the class has a `_classThis` assignment block, this helper will be injected after that block.
if classHasExplicitlyAssignedName(emitContext, node) {
return node
}
factory := emitContext.Factory
namedEvaluationBlock := createClassNamedEvaluationHelperBlock(emitContext, assignedName, thisExpression)
if node.Name() != nil {
emitContext.SetSourceMapRange(namedEvaluationBlock.Body().Statements()[0], node.Name().Loc)
}
insertionIndex := slices.IndexFunc(node.Members(), func(n *ast.Node) bool {
return isClassThisAssignmentBlock(emitContext, n)
}) + 1
leading := slices.Clone(node.Members()[:insertionIndex])
trailing := slices.Clone(node.Members()[insertionIndex:])
var members []*ast.ClassElement
members = append(members, leading...)
members = append(members, namedEvaluationBlock)
members = append(members, trailing...)
membersList := factory.NewNodeList(members)
membersList.Loc = node.MemberList().Loc
oldNode := node
if ast.IsClassDeclaration(node) {
node = factory.UpdateClassDeclaration(
node.AsClassDeclaration(),
node.Modifiers(),
node.Name(),
node.TypeParameterList(),
node.AsClassDeclaration().HeritageClauses,
membersList,
)
} else {
node = factory.UpdateClassExpression(
node.AsClassExpression(),
node.Modifiers(),
node.Name(),
node.TypeParameterList(),
node.AsClassExpression().HeritageClauses,
membersList,
)
}
emitContext.SetAssignedName(node, assignedName)
// Transfer ClassThis from old to new node, since UpdateClassExpression creates
// a new node that won't have ClassThis set on it.
if ct := emitContext.ClassThis(oldNode); ct != nil {
emitContext.SetClassThis(node, ct)
}
return node
}
func finishTransformNamedEvaluation(
emitContext *printer.EmitContext,
expression *ast.Node, // WrappedExpression<AnonymousFunctionDefinition>,
assignedName *ast.Expression,
ignoreEmptyStringLiteral bool,
) *ast.Expression {
if ignoreEmptyStringLiteral && ast.IsStringLiteral(assignedName) && len(assignedName.Text()) == 0 {
return expression
}
factory := emitContext.Factory
innerExpression := ast.SkipOuterExpressions(expression, ast.OEKAll)
var updatedExpression *ast.Expression
if ast.IsClassExpression(innerExpression) {
updatedExpression = injectClassNamedEvaluationHelperBlockIfMissing(emitContext, innerExpression, assignedName, nil /*thisExpression*/)
} else {
updatedExpression = factory.NewSetFunctionNameHelper(innerExpression, assignedName, "" /*prefix*/)
}
return factory.RestoreOuterExpressions(expression, updatedExpression, ast.OEKAll)
}
func transformNamedEvaluationOfPropertyAssignment(context *printer.EmitContext, node *ast.PropertyAssignment /*NamedEvaluation & PropertyAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.2.5.5 RS: PropertyDefinitionEvaluation
// PropertyAssignment : PropertyName `:` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and _isProtoSetter_ is *false*, then
// a. Let _popValue_ be ? NamedEvaluation of |AssignmentExpression| with argument _propKey_.
// ...
factory := context.Factory
assignedName, name := getAssignedNameOfPropertyName(context, node.Name(), assignedNameText)
initializer := finishTransformNamedEvaluation(context, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdatePropertyAssignment(node, nil /*modifiers*/, name, nil /*postfixToken*/, nil /*typeNode*/, initializer)
}
func transformNamedEvaluationOfShorthandAssignmentProperty(emitContext *printer.EmitContext, node *ast.ShorthandPropertyAssignment /*NamedEvaluation & ShorthandPropertyAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.15.5.3 RS: PropertyDestructuringAssignmentEvaluation
// AssignmentProperty : IdentifierReference Initializer?
// ...
// 4. If |Initializer?| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _P_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.ObjectAssignmentInitializer)
}
objectAssignmentInitializer := finishTransformNamedEvaluation(emitContext, node.ObjectAssignmentInitializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateShorthandPropertyAssignment(
node,
nil, /*modifiers*/
node.Name(),
nil, /*postfixToken*/
nil, /*typeNode*/
node.EqualsToken,
objectAssignmentInitializer,
)
}
func transformNamedEvaluationOfVariableDeclaration(emitContext *printer.EmitContext, node *ast.VariableDeclaration /*NamedEvaluation & VariableDeclaration*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 14.3.1.2 RS: Evaluation
// LexicalBinding : BindingIdentifier Initializer
// ...
// 3. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
//
// 14.3.2.1 RS: Evaluation
// VariableDeclaration : BindingIdentifier Initializer
// ...
// 3. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.Initializer)
}
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateVariableDeclaration(
node,
node.Name(),
nil, /*exclamationToken*/
nil, /*typeNode*/
initializer,
)
}
func transformNamedEvaluationOfParameterDeclaration(emitContext *printer.EmitContext, node *ast.ParameterDeclaration /*NamedEvaluation & ParameterDeclaration*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 8.6.3 RS: IteratorBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 5. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
//
// 14.3.3.3 RS: KeyedBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 4. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.Initializer)
}
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateParameterDeclaration(
node,
nil, /*modifiers*/
node.DotDotDotToken,
node.Name(),
nil, /*questionToken*/
nil, /*typeNode*/
initializer,
)
}
func transformNamedEvaluationOfBindingElement(emitContext *printer.EmitContext, node *ast.BindingElement /*NamedEvaluation & BindingElement*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 8.6.3 RS: IteratorBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 5. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
//
// 14.3.3.3 RS: KeyedBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 4. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.Initializer)
}
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateBindingElement(
node,
node.DotDotDotToken,
node.PropertyName,
node.Name(),
initializer,
)
}
func transformNamedEvaluationOfPropertyDeclaration(emitContext *printer.EmitContext, node *ast.PropertyDeclaration /*NamedEvaluation & PropertyDeclaration*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 10.2.1.3 RS: EvaluateBody
// Initializer : `=` AssignmentExpression
// ...
// 3. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |Initializer| with argument _functionObject_.[[ClassFieldInitializerName]].
// ...
factory := emitContext.Factory
assignedName, name := getAssignedNameOfPropertyName(emitContext, node.Name(), assignedNameText)
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdatePropertyDeclaration(
node,
node.Modifiers(),
name,
nil, /*postfixToken*/
nil, /*typeNode*/
initializer,
)
}
func transformNamedEvaluationOfAssignmentExpression(emitContext *printer.EmitContext, node *ast.BinaryExpression /*NamedEvaluation & BinaryExpression*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.15.2 RS: Evaluation
// AssignmentExpression : LeftHandSideExpression `=` AssignmentExpression
// 1. If |LeftHandSideExpression| is neither an |ObjectLiteral| nor an |ArrayLiteral|, then
// a. Let _lref_ be ? Evaluation of |LeftHandSideExpression|.
// b. If IsAnonymousFunctionDefinition(|AssignmentExpression|) and IsIdentifierRef of |LeftHandSideExpression| are both *true*, then
// i. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
//
// AssignmentExpression : LeftHandSideExpression `&&=` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then
// a. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
//
// AssignmentExpression : LeftHandSideExpression `||=` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then
// a. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
//
// AssignmentExpression : LeftHandSideExpression `??=` AssignmentExpression
// ...
// 4. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then
// a. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Left, node.Right)
}
right := finishTransformNamedEvaluation(emitContext, node.Right, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateBinaryExpression(
node,
nil, /*modifiers*/
node.Left,
nil, /*typeNode*/
node.OperatorToken,
right,
)
}
func transformNamedEvaluationOfExportAssignment(emitContext *printer.EmitContext, node *ast.ExportAssignment /*NamedEvaluation & ExportAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 16.2.3.7 RS: Evaluation
// ExportDeclaration : `export` `default` AssignmentExpression `;`
// 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |AssignmentExpression| with argument `"default"`.
// ...
// NOTE: Since emit for `export =` translates to `module.exports = ...`, the assigned name of the class or function
// is `""`.
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else if node.IsExportEquals {
assignedName = factory.NewStringLiteral("", ast.TokenFlagsNone)
} else {
assignedName = factory.NewStringLiteral("default", ast.TokenFlagsNone)
}
expression := finishTransformNamedEvaluation(emitContext, node.Expression, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateExportAssignment(
node,
nil, /*modifiers*/
node.IsExportEquals,
nil, /*typeNode*/
expression,
)
}
// Performs a shallow transformation of a `NamedEvaluation` node, such that a valid name will be assigned.
func transformNamedEvaluation(context *printer.EmitContext, node *ast.Node /*NamedEvaluation*/, ignoreEmptyStringLiteral bool, assignedName string) *ast.Expression {
switch node.Kind {
case ast.KindPropertyAssignment:
return transformNamedEvaluationOfPropertyAssignment(context, node.AsPropertyAssignment(), ignoreEmptyStringLiteral, assignedName)
case ast.KindShorthandPropertyAssignment:
return transformNamedEvaluationOfShorthandAssignmentProperty(context, node.AsShorthandPropertyAssignment(), ignoreEmptyStringLiteral, assignedName)
case ast.KindVariableDeclaration:
return transformNamedEvaluationOfVariableDeclaration(context, node.AsVariableDeclaration(), ignoreEmptyStringLiteral, assignedName)
case ast.KindParameter:
return transformNamedEvaluationOfParameterDeclaration(context, node.AsParameterDeclaration(), ignoreEmptyStringLiteral, assignedName)
case ast.KindBindingElement:
return transformNamedEvaluationOfBindingElement(context, node.AsBindingElement(), ignoreEmptyStringLiteral, assignedName)
case ast.KindPropertyDeclaration:
return transformNamedEvaluationOfPropertyDeclaration(context, node.AsPropertyDeclaration(), ignoreEmptyStringLiteral, assignedName)
case ast.KindBinaryExpression:
return transformNamedEvaluationOfAssignmentExpression(context, node.AsBinaryExpression(), ignoreEmptyStringLiteral, assignedName)
case ast.KindExportAssignment:
return transformNamedEvaluationOfExportAssignment(context, node.AsExportAssignment(), ignoreEmptyStringLiteral, assignedName)
default:
debug.Fail("Unhandled case in transformNamedEvaluation")
return node
}
}

View File

@@ -0,0 +1,49 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type nullishCoalescingTransformer struct {
transformers.Transformer
}
func (ch *nullishCoalescingTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsNullishCoalescing == 0 {
return node
}
switch node.Kind {
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *nullishCoalescingTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node {
switch node.OperatorToken.Kind {
case ast.KindQuestionQuestionToken:
left := ch.Visitor().VisitNode(node.Left)
right := left
if !transformers.IsSimpleCopiableExpression(left) {
right = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(right)
left = ch.Factory().NewAssignmentExpression(right, left)
}
return ch.Factory().NewConditionalExpression(
createNotNullCondition(ch.EmitContext(), left, right, false),
ch.Factory().NewToken(ast.KindQuestionToken),
right,
ch.Factory().NewToken(ast.KindColonToken),
ch.Visitor().VisitNode(node.Right),
)
default:
return ch.Visitor().VisitEachChild(node.AsNode())
}
}
func newNullishCoalescingTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &nullishCoalescingTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,593 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type objectRestSpreadTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
inExportedVariableStatement bool
expressionResultIsUnused bool
parametersWithPrecedingObjectRestOrSpread map[*ast.Node]struct{}
}
func (ch *objectRestSpreadTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsESObjectRestOrSpread == 0 && ch.parametersWithPrecedingObjectRestOrSpread == nil {
return node
}
// Save the expressionResultIsUnused flag set by the parent for this node,
// then reset to false for children (the default). Specific cases below override as needed.
expressionResultIsUnused := ch.expressionResultIsUnused
ch.expressionResultIsUnused = false
defer func() { ch.expressionResultIsUnused = expressionResultIsUnused }()
switch node.Kind {
case ast.KindSourceFile:
return ch.visitSourceFile(node.AsSourceFile())
case ast.KindObjectLiteralExpression:
return ch.visitObjectLiteralExpression(node.AsObjectLiteralExpression())
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression(), expressionResultIsUnused)
case ast.KindExpressionStatement:
ch.expressionResultIsUnused = true
return ch.Visitor().VisitEachChild(node)
case ast.KindParenthesizedExpression:
ch.expressionResultIsUnused = expressionResultIsUnused
return ch.Visitor().VisitEachChild(node)
case ast.KindForOfStatement:
return ch.visitForOftatement(node.AsForInOrOfStatement())
case ast.KindVariableStatement:
return ch.visitVariableStatement(node.AsVariableStatement())
case ast.KindVariableDeclaration:
return ch.visitVariableDeclaration(node.AsVariableDeclaration())
case ast.KindCatchClause:
return ch.visitCatchClause(node.AsCatchClause())
case ast.KindParameter:
return ch.visitParameter(node.AsParameterDeclaration())
case ast.KindConstructor:
return ch.visitContructorDeclaration(node.AsConstructorDeclaration())
case ast.KindGetAccessor:
return ch.visitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
return ch.visitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
case ast.KindMethodDeclaration:
return ch.visitMethodDeclaration(node.AsMethodDeclaration())
case ast.KindFunctionDeclaration:
return ch.visitFunctionDeclaration(node.AsFunctionDeclaration())
case ast.KindArrowFunction:
return ch.visitArrowFunction(node.AsArrowFunction())
case ast.KindFunctionExpression:
return ch.visitFunctionExpression(node.AsFunctionExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *objectRestSpreadTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
visited := ch.Visitor().VisitEachChild(node.AsNode())
ch.EmitContext().AddEmitHelper(visited.AsNode(), ch.EmitContext().ReadEmitHelpers()...)
return visited
}
func (ch *objectRestSpreadTransformer) visitParameter(node *ast.ParameterDeclaration) *ast.Node {
if ch.parametersWithPrecedingObjectRestOrSpread != nil {
if _, ok := ch.parametersWithPrecedingObjectRestOrSpread[node.AsNode()]; ok {
name := node.Name()
if ast.IsBindingPattern(name) {
name = ch.Factory().NewGeneratedNameForNode(node.AsNode())
}
return ch.Factory().UpdateParameterDeclaration(
node,
nil,
node.DotDotDotToken,
name,
nil,
nil,
nil,
)
}
}
if node.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
// Binding patterns are converted into a generated name and are
// evaluated inside the function body.
return ch.Factory().UpdateParameterDeclaration(
node,
nil,
node.DotDotDotToken,
ch.Factory().NewGeneratedNameForNode(node.AsNode()),
nil,
nil,
ch.Visitor().VisitNode(node.Initializer),
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) collectParametersWithPrecedingObjectRestOrSpread(node *ast.Node) map[*ast.Node]struct{} {
var result map[*ast.Node]struct{}
for _, parameter := range node.Parameters() {
if result != nil {
result[parameter] = struct{}{}
} else if parameter.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
result = make(map[*ast.Node]struct{})
}
}
return result
}
type oldParamScope map[*ast.Node]struct{}
func (ch *objectRestSpreadTransformer) enterParameterListContext(node *ast.Node) oldParamScope {
old := ch.parametersWithPrecedingObjectRestOrSpread
ch.parametersWithPrecedingObjectRestOrSpread = ch.collectParametersWithPrecedingObjectRestOrSpread(node)
return oldParamScope(old)
}
func (ch *objectRestSpreadTransformer) exitParameterListContext(scope oldParamScope) {
ch.parametersWithPrecedingObjectRestOrSpread = map[*ast.Node]struct{}(scope)
}
func (ch *objectRestSpreadTransformer) visitContructorDeclaration(node *ast.ConstructorDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateConstructorDeclaration(
node,
node.Modifiers(),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitGetAccessorDeclaration(node *ast.GetAccessorDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateGetAccessorDeclaration(
node,
node.Modifiers(),
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitSetAccessorDeclaration(node *ast.SetAccessorDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateSetAccessorDeclaration(
node,
node.Modifiers(),
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitMethodDeclaration(node *ast.MethodDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateMethodDeclaration(
node,
node.Modifiers(),
node.AsteriskToken,
ch.Visitor().VisitNode(node.Name()),
node.PostfixToken,
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitFunctionDeclaration(node *ast.FunctionDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateFunctionDeclaration(
node,
node.Modifiers(),
node.AsteriskToken,
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitArrowFunction(node *ast.ArrowFunction) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateArrowFunction(
node,
node.Modifiers(),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
node.EqualsGreaterThanToken,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitFunctionExpression(node *ast.FunctionExpression) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateFunctionExpression(
node,
node.Modifiers(),
node.AsteriskToken,
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) transformFunctionBody(node *ast.Node) *ast.Node {
// EmitContext().VisitFunctionBody is not used here because this transformer needs to inject
// object rest assignments between visiting the body and merging the variable environment.
ch.EmitContext().StartVariableEnvironment()
body := ch.Visitor().VisitNode(node.Body())
extras := ch.EmitContext().EndVariableEnvironment()
ch.EmitContext().StartVariableEnvironment()
newStatements := ch.collectObjectRestAssignments(node)
extras = ch.EmitContext().EndAndMergeVariableEnvironment(extras)
if len(newStatements) == 0 && len(extras) == 0 {
return body
}
if body == nil {
body = ch.Factory().NewBlock(ch.Factory().NewNodeList([]*ast.Node{}), true)
}
var prefix []*ast.Node
var suffix []*ast.Node
if ast.IsBlock(body) {
custom := false
for i, statement := range body.Statements() {
if !custom && ast.IsPrologueDirective(statement) {
prefix = append(prefix, statement)
} else if ch.EmitContext().EmitFlags(statement)&printer.EFCustomPrologue != 0 {
custom = true
prefix = append(prefix, statement)
} else {
suffix = body.Statements()[i:]
break
}
}
} else {
ret := ch.Factory().NewReturnStatement(body)
ret.Loc = body.Loc
list := ch.Factory().NewNodeList([]*ast.Node{})
list.Loc = body.Loc
body = ch.Factory().NewBlock(list, true)
suffix = append(suffix, ret)
}
newStatementList := ch.Factory().NewNodeList(append(append(append(prefix, extras...), newStatements...), suffix...))
newStatementList.Loc = body.StatementList().Loc
return ch.Factory().UpdateBlock(body.AsBlock(), newStatementList, body.AsBlock().MultiLine)
}
func (ch *objectRestSpreadTransformer) collectObjectRestAssignments(node *ast.Node) []*ast.Node {
containsPrecedingObjectRestOrSpread := false
var results []*ast.Node
for _, parameter := range node.Parameters() {
if containsPrecedingObjectRestOrSpread {
if ast.IsBindingPattern(parameter.Name()) {
// In cases where a binding pattern is simply '[]' or '{}',
// we usually don't want to emit a var declaration; however, in the presence
// of an initializer, we must emit that expression to preserve side effects.
if len(parameter.Name().Elements()) > 0 {
declarations := transformers.FlattenDestructuringBinding(
&ch.Transformer,
parameter, ch.Factory().NewGeneratedNameForNode(parameter),
transformers.FlattenLevelAll, false, false,
)
if declarations != nil {
declarationList := ch.Factory().NewVariableDeclarationList(ch.Factory().NewNodeList([]*ast.Node{}), ast.NodeFlagsNone)
decls := []*ast.Node{declarations}
if declarations.Kind == ast.KindSyntaxList {
decls = declarations.AsSyntaxList().Children
}
declarationList.AsVariableDeclarationList().Declarations.Nodes = append(declarationList.AsVariableDeclarationList().Declarations.Nodes, decls...)
statement := ch.Factory().NewVariableStatement(nil, declarationList)
ch.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
results = append(results, statement)
}
} else if parameter.Initializer() != nil {
name := ch.Factory().NewGeneratedNameForNode(parameter)
initializer := ch.Visitor().VisitNode(parameter.Initializer())
assignment := ch.Factory().NewAssignmentExpression(name, initializer)
statement := ch.Factory().NewExpressionStatement(assignment)
ch.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
results = append(results, statement)
}
} else if parameter.Initializer() != nil {
// Converts a parameter initializer into a function body statement, i.e.:
//
// function f(x = 1) { }
//
// becomes
//
// function f(x) {
// if (typeof x === "undefined") { x = 1; }
// }
name := parameter.Name().Clone(ch.Factory())
name.Loc = parameter.Name().Loc
ch.EmitContext().AddEmitFlags(name, printer.EFNoSourceMap)
initializer := ch.Visitor().VisitNode(parameter.Initializer())
ch.EmitContext().AddEmitFlags(initializer, printer.EFNoSourceMap|printer.EFNoComments)
assignment := ch.Factory().NewAssignmentExpression(name, initializer)
assignment.Loc = parameter.Loc
ch.EmitContext().AddEmitFlags(assignment, printer.EFNoComments)
block := ch.Factory().NewBlock(ch.Factory().NewNodeList([]*ast.Node{ch.Factory().NewExpressionStatement(assignment)}), false)
block.Loc = parameter.Loc
ch.EmitContext().AddEmitFlags(block, printer.EFSingleLine|printer.EFNoTrailingSourceMap|printer.EFNoTokenSourceMaps|printer.EFNoComments)
typeCheck := ch.Factory().NewTypeCheck(name.Clone(ch.Factory()), "undefined")
statement := ch.Factory().NewIfStatement(typeCheck, block, nil)
statement.Loc = parameter.Loc
ch.EmitContext().AddEmitFlags(statement, printer.EFNoTokenSourceMaps|printer.EFNoTrailingSourceMap|printer.EFCustomPrologue|printer.EFNoComments|printer.EFStartOnNewLine)
results = append(results, statement)
}
} else if parameter.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
containsPrecedingObjectRestOrSpread = true
declarations := transformers.FlattenDestructuringBinding(
&ch.Transformer,
parameter, ch.Factory().NewGeneratedNameForNode(parameter),
transformers.FlattenLevelObjectRest, false, true,
)
if declarations != nil {
declarationList := ch.Factory().NewVariableDeclarationList(ch.Factory().NewNodeList([]*ast.Node{}), ast.NodeFlagsNone)
decls := []*ast.Node{declarations}
if declarations.Kind == ast.KindSyntaxList {
decls = declarations.AsSyntaxList().Children
}
declarationList.AsVariableDeclarationList().Declarations.Nodes = append(declarationList.AsVariableDeclarationList().Declarations.Nodes, decls...)
statement := ch.Factory().NewVariableStatement(nil, declarationList)
ch.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
results = append(results, statement)
}
}
}
return results
}
func (ch *objectRestSpreadTransformer) visitCatchClause(node *ast.CatchClause) *ast.Node {
if node.VariableDeclaration != nil && ast.IsBindingPattern(node.VariableDeclaration.Name()) && node.VariableDeclaration.Name().SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
name := ch.Factory().NewGeneratedNameForNode(node.VariableDeclaration.Name())
updatedDecl := ch.Factory().UpdateVariableDeclaration(node.VariableDeclaration.AsVariableDeclaration(), node.VariableDeclaration.Name(), nil, nil, name)
visitedBindings := transformers.FlattenDestructuringBinding(
&ch.Transformer,
updatedDecl, nil,
transformers.FlattenLevelObjectRest, false, false,
)
block := ch.Visitor().VisitNode(node.Block)
if visitedBindings != nil {
var decls []*ast.Node
if visitedBindings.Kind == ast.KindSyntaxList {
decls = visitedBindings.AsSyntaxList().Children
} else {
decls = []*ast.Node{visitedBindings}
}
newStatement := ch.Factory().NewVariableStatement(nil, ch.Factory().NewVariableDeclarationList(ch.Factory().NewNodeList(decls), ast.NodeFlagsNone))
statements := []*ast.Node{newStatement}
statements = append(statements, block.Statements()...)
statementList := ch.Factory().NewNodeList(statements)
statementList.Loc = block.StatementList().Loc
block = ch.Factory().UpdateBlock(block.AsBlock(), statementList, block.AsBlock().MultiLine)
}
return ch.Factory().UpdateCatchClause(
node,
ch.Factory().UpdateVariableDeclaration(node.VariableDeclaration.AsVariableDeclaration(), name, nil, nil, nil),
block,
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitVariableStatement(node *ast.VariableStatement) *ast.Node {
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
oldInExportedVariableStatement := ch.inExportedVariableStatement
ch.inExportedVariableStatement = true
result := ch.Visitor().VisitEachChild(node.AsNode())
ch.inExportedVariableStatement = oldInExportedVariableStatement
return result
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitVariableDeclaration(node *ast.VariableDeclaration) *ast.Node {
if ch.inExportedVariableStatement {
ch.inExportedVariableStatement = false
result := ch.visitVariableDeclarationWorker(node, true)
ch.inExportedVariableStatement = true
return result
}
return ch.visitVariableDeclarationWorker(node, false)
}
func (ch *objectRestSpreadTransformer) visitVariableDeclarationWorker(node *ast.VariableDeclaration, exported bool) *ast.Node {
// If we are here it is because the name contains a binding pattern with a rest somewhere in it.
if ast.IsBindingPattern(node.Name()) && node.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
return transformers.FlattenDestructuringBinding(
&ch.Transformer,
node.AsNode(), nil,
transformers.FlattenLevelObjectRest, exported, false,
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitForOftatement(node *ast.ForInOrOfStatement) *ast.Node {
if node.Initializer.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 || (ast.IsAssignmentPattern(node.Initializer) && ast.ContainsObjectRestOrSpread(node.Initializer)) {
initializerWithoutParens := ast.SkipParentheses(node.Initializer)
if ast.IsVariableDeclarationList(initializerWithoutParens) || ast.IsAssignmentPattern(initializerWithoutParens) {
var bodyLocation core.TextRange
var statementsLocation core.TextRange
temp := ch.Factory().NewTempVariable()
res := ch.Visitor().VisitNode(ch.Factory().CreateForOfBindingStatement(initializerWithoutParens, temp))
statements := make([]*ast.Node, 0, 1)
if res != nil {
statements = append(statements, res)
}
if ast.IsBlock(node.Statement) {
for _, statement := range node.Statement.Statements() {
visited := ch.Visitor().VisitEachChild(statement)
if visited != nil {
statements = append(statements, visited)
}
}
bodyLocation = node.Statement.Loc
statementsLocation = node.Statement.StatementList().Loc
} else if node.Statement != nil {
statements = append(statements, ch.Visitor().VisitEachChild(node.Statement))
bodyLocation = node.Statement.Loc
statementsLocation = node.Statement.Loc
}
list := ch.Factory().NewVariableDeclarationList(
ch.Factory().NewNodeList([]*ast.Node{ch.Factory().NewVariableDeclaration(temp, nil, nil, nil)}),
ast.NodeFlagsLet,
)
list.Loc = node.Initializer.Loc
expr := ch.Visitor().VisitEachChild(node.Expression)
statementsList := ch.Factory().NewNodeList(statements)
statementsList.Loc = statementsLocation
block := ch.Factory().NewBlock(statementsList, true)
block.Loc = bodyLocation
return ch.Factory().UpdateForInOrOfStatement(
node,
node.AwaitModifier,
list,
expr,
block,
)
}
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitBinaryExpression(node *ast.BinaryExpression, expressionResultIsUnused bool) *ast.Node {
if ast.IsDestructuringAssignment(node.AsNode()) && ast.ContainsObjectRestOrSpread(node.Left) {
return transformers.FlattenDestructuringAssignment(
&ch.Transformer,
node.AsNode(), !expressionResultIsUnused,
transformers.FlattenLevelObjectRest, nil,
)
}
if node.OperatorToken.Kind == ast.KindCommaToken {
ch.expressionResultIsUnused = true
left := ch.Visitor().VisitNode(node.Left)
ch.expressionResultIsUnused = expressionResultIsUnused
right := ch.Visitor().VisitNode(node.Right)
return ch.Factory().UpdateBinaryExpression(node, nil, left, nil, node.OperatorToken, right)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitObjectLiteralExpression(node *ast.ObjectLiteralExpression) *ast.Node {
if (node.SubtreeFacts() & ast.SubtreeContainsObjectRestOrSpread) == 0 {
return ch.Visitor().VisitEachChild(node.AsNode())
}
// spread elements emit like so:
// non-spread elements are chunked together into object literals, and then all are passed to __assign:
// { a, ...o, b } => __assign(__assign({a}, o), {b});
// If the first element is a spread element, then the first argument to __assign is {}:
// { ...o, a, b, ...o2 } => __assign(__assign(__assign({}, o), {a, b}), o2)
//
// We cannot call __assign with more than two elements, since any element could cause side effects. For
// example:
// var k = { a: 1, b: 2 };
// var o = { a: 3, ...k, b: k.a++ };
// // expected: { a: 1, b: 1 }
// If we translate the above to `__assign({ a: 3 }, k, { b: k.a++ })`, the `k.a++` will evaluate before
// `k` is spread and we end up with `{ a: 2, b: 1 }`.
//
// This also occurs for spread elements, not just property assignments:
// var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
// var l = { c: 3 };
// var o = { ...k, ...l };
// // expected: { a: 1, b: 2, z: 9 }
// If we translate the above to `__assign({}, k, l)`, the `l` will evaluate before `k` is spread and we
// end up with `{ a: 1, b: 2, c: 3 }`
objects := ch.chunkObjectLiteralElements(node.Properties)
if len(objects) > 0 && objects[0].Kind != ast.KindObjectLiteralExpression {
objects = append([]*ast.Node{ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(nil), false)}, objects...)
}
expression := objects[0]
if len(objects) > 1 {
for i, obj := range objects {
if i == 0 {
continue
}
expression = ch.Factory().NewAssignHelper([]*ast.Node{expression, obj}, ch.compilerOptions.GetEmitScriptTarget())
}
return expression
}
return ch.Factory().NewAssignHelper(objects, ch.compilerOptions.GetEmitScriptTarget())
}
func (ch *objectRestSpreadTransformer) chunkObjectLiteralElements(list *ast.NodeList) []*ast.Node {
if list == nil || len(list.Nodes) == 0 {
return nil
}
elements := list.Nodes
var chunkObject []*ast.Node
objects := make([]*ast.Node, 0, 1)
for _, e := range elements {
if e.Kind == ast.KindSpreadAssignment {
if len(chunkObject) > 0 {
objects = append(objects, ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(chunkObject), false))
chunkObject = nil
}
target := e.Expression()
objects = append(objects, ch.Visitor().VisitNode(target))
} else {
var elem *ast.Node
if e.Kind == ast.KindPropertyAssignment {
elem = ch.Factory().NewPropertyAssignment(nil, e.Name(), nil, nil, ch.Visitor().VisitNode(e.Initializer()))
} else {
elem = ch.Visitor().VisitNode(e)
}
chunkObject = append(chunkObject, elem)
}
}
if len(chunkObject) > 0 {
objects = append(objects, ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(chunkObject), false))
}
return objects
}
func newObjectRestSpreadTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &objectRestSpreadTransformer{compilerOptions: opts.CompilerOptions}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,37 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type optionalCatchTransformer struct {
transformers.Transformer
}
func (ch *optionalCatchTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsMissingCatchClauseVariable == 0 {
return node
}
switch node.Kind {
case ast.KindCatchClause:
return ch.visitCatchClause(node.AsCatchClause())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *optionalCatchTransformer) visitCatchClause(node *ast.CatchClause) *ast.Node {
if node.VariableDeclaration == nil {
return ch.Factory().NewCatchClause(
ch.Factory().NewVariableDeclaration(ch.Factory().NewTempVariable(), nil, nil, nil),
ch.Visitor().Visit(node.Block),
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func newOptionalCatchTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &optionalCatchTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,240 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type optionalChainTransformer struct {
transformers.Transformer
}
func (ch *optionalChainTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsOptionalChaining == 0 {
return node
}
switch node.Kind {
case ast.KindCallExpression:
return ch.visitCallExpression(node.AsCallExpression(), false)
case ast.KindPropertyAccessExpression,
ast.KindElementAccessExpression:
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
return ch.visitOptionalExpression(node, false, false)
}
return ch.Visitor().VisitEachChild(node)
case ast.KindDeleteExpression:
return ch.visitDeleteExpression(node.AsDeleteExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *optionalChainTransformer) visitCallExpression(node *ast.CallExpression, captureThisArg bool) *ast.Node {
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return ch.visitOptionalExpression(node.AsNode(), captureThisArg, false)
}
if ast.IsParenthesizedExpression(node.Expression) {
unwrapped := ast.SkipParentheses(node.Expression)
if unwrapped.Flags&ast.NodeFlagsOptionalChain != 0 {
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
expression := ch.visitParenthesizedExpression(node.Expression.AsParenthesizedExpression(), true, false)
args := ch.Visitor().VisitNodes(node.Arguments)
if ast.IsSyntheticReferenceExpression(expression) {
res := ch.Factory().NewFunctionCallCall(expression.AsSyntheticReferenceExpression().Expression, expression.AsSyntheticReferenceExpression().ThisArg, args.Nodes)
res.Loc = node.Loc
ch.EmitContext().SetOriginal(res, node.AsNode())
return res
}
return ch.Factory().UpdateCallExpression(node, expression, nil /*questionDotToken*/, nil /*typeArguments*/, args, node.Flags)
}
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *optionalChainTransformer) visitParenthesizedExpression(node *ast.ParenthesizedExpression, captureThisArg bool, isDelete bool) *ast.Node {
expr := ch.visitNonOptionalExpression(node.Expression, captureThisArg, isDelete)
if ast.IsSyntheticReferenceExpression(expr) {
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
synth := expr.AsSyntheticReferenceExpression()
res := ch.Factory().NewSyntheticReferenceExpression(ch.Factory().UpdateParenthesizedExpression(node, synth.Expression), synth.ThisArg)
ch.EmitContext().SetOriginal(res, node.AsNode())
return res
}
return ch.Factory().UpdateParenthesizedExpression(node, expr)
}
func (ch *optionalChainTransformer) visitPropertyOrElementAccessExpression(node *ast.Expression, captureThisArg bool, isDelete bool) *ast.Expression {
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return ch.visitOptionalExpression(node.AsNode(), captureThisArg, isDelete)
}
expression := ch.Visitor().VisitNode(node.Expression())
debug.Assert(expression == nil || !ast.IsSyntheticReferenceExpression(expression))
var thisArg *ast.Expression
if captureThisArg {
if !transformers.IsSimpleCopiableExpression(expression) {
thisArg = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(thisArg)
expression = ch.Factory().NewAssignmentExpression(thisArg, expression)
} else {
thisArg = expression
}
}
if node.Kind == ast.KindPropertyAccessExpression {
p := node.AsPropertyAccessExpression()
expression = ch.Factory().UpdatePropertyAccessExpression(p, expression, nil /*questionDotToken*/, ch.Visitor().VisitNode(p.Name()), p.Flags)
} else {
p := node.AsElementAccessExpression()
expression = ch.Factory().UpdateElementAccessExpression(p, expression, nil, ch.Visitor().VisitNode(p.AsElementAccessExpression().ArgumentExpression), p.Flags)
}
if thisArg != nil {
res := ch.Factory().NewSyntheticReferenceExpression(expression, thisArg)
ch.EmitContext().SetOriginal(res, node.AsNode())
return res
}
return expression
}
func (ch *optionalChainTransformer) visitDeleteExpression(node *ast.DeleteExpression) *ast.Node {
unwrapped := ast.SkipParentheses(node.Expression)
if unwrapped.Flags&ast.NodeFlagsOptionalChain != 0 {
return ch.visitNonOptionalExpression(node.Expression, false, true)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *optionalChainTransformer) visitNonOptionalExpression(node *ast.Expression, captureThisArg bool, isDelete bool) *ast.Expression {
switch node.Kind {
case ast.KindParenthesizedExpression:
return ch.visitParenthesizedExpression(node.AsParenthesizedExpression(), captureThisArg, isDelete)
case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression:
return ch.visitPropertyOrElementAccessExpression(node, captureThisArg, isDelete)
case ast.KindCallExpression:
return ch.visitCallExpression(node.AsCallExpression(), captureThisArg)
default:
return ch.Visitor().VisitNode(node.AsNode())
}
}
type flattenResult struct {
expression *ast.Expression
chain []*ast.Node
}
func isNonNullChain(node *ast.Node) bool {
return ast.IsNonNullExpression(node) && node.Flags&ast.NodeFlagsOptionalChain != 0
}
func flattenChain(chain *ast.Node) flattenResult {
debug.Assert(!isNonNullChain(chain))
links := []*ast.Node{chain}
for !ast.IsTaggedTemplateExpression(chain) && chain.QuestionDotToken() == nil {
chain = ast.SkipPartiallyEmittedExpressions(chain.Expression())
debug.Assert(!isNonNullChain(chain))
links = append([]*ast.Node{chain}, links...)
}
return flattenResult{chain.Expression(), links}
}
func isCallChain(node *ast.Node) bool {
return ast.IsCallExpression(node) && node.Flags&ast.NodeFlagsOptionalChain != 0
}
func (ch *optionalChainTransformer) visitOptionalExpression(node *ast.Node, captureThisArg bool, isDelete bool) *ast.Node {
r := flattenChain(node)
expression := r.expression
chain := r.chain
left := ch.visitNonOptionalExpression(ast.SkipPartiallyEmittedExpressions(expression), isCallChain(chain[0]), false)
var leftThisArg *ast.Expression
capturedLeft := left
if ast.IsSyntheticReferenceExpression(left) {
leftThisArg = left.AsSyntheticReferenceExpression().ThisArg
capturedLeft = left.AsSyntheticReferenceExpression().Expression
}
leftExpression := ch.Factory().RestoreOuterExpressions(expression, capturedLeft, ast.OEKPartiallyEmittedExpressions)
if !transformers.IsSimpleCopiableExpression(capturedLeft) {
capturedLeft = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(capturedLeft)
leftExpression = ch.Factory().NewAssignmentExpression(capturedLeft, leftExpression)
}
rightExpression := capturedLeft
var thisArg *ast.Expression
for i, segment := range chain {
switch segment.Kind {
case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression:
if i == len(chain)-1 && captureThisArg {
if !transformers.IsSimpleCopiableExpression(rightExpression) {
thisArg = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(thisArg)
rightExpression = ch.Factory().NewAssignmentExpression(thisArg, rightExpression)
} else {
thisArg = rightExpression
}
}
if segment.Kind == ast.KindElementAccessExpression {
rightExpression = ch.Factory().NewElementAccessExpression(rightExpression, nil, ch.Visitor().VisitNode(segment.AsElementAccessExpression().ArgumentExpression), ast.NodeFlagsNone)
} else {
rightExpression = ch.Factory().NewPropertyAccessExpression(rightExpression, nil, ch.Visitor().VisitNode(segment.AsPropertyAccessExpression().Name()), ast.NodeFlagsNone)
}
case ast.KindCallExpression:
if i == 0 && leftThisArg != nil {
if !ch.EmitContext().HasAutoGenerateInfo(leftThisArg) {
leftThisArg = leftThisArg.Clone(ch.Factory())
ch.EmitContext().AddEmitFlags(leftThisArg, printer.EFNoComments)
}
callThisArg := leftThisArg
if leftThisArg.Kind == ast.KindSuperKeyword {
callThisArg = ch.Factory().NewThisExpression()
}
rightExpression = ch.Factory().NewFunctionCallCall(rightExpression, callThisArg, ch.Visitor().VisitNodes(segment.ArgumentList()).Nodes)
} else {
rightExpression = ch.Factory().NewCallExpression(
rightExpression,
nil,
nil,
ch.Visitor().VisitNodes(segment.ArgumentList()),
ast.NodeFlagsNone,
)
}
}
ch.EmitContext().SetOriginal(rightExpression, segment)
}
var target *ast.Node
if isDelete {
target = ch.Factory().NewConditionalExpression(
createNotNullCondition(ch.EmitContext(), leftExpression, capturedLeft, true),
ch.Factory().NewToken(ast.KindQuestionToken),
ch.Factory().NewTrueExpression(),
ch.Factory().NewToken(ast.KindColonToken),
ch.Factory().NewDeleteExpression(rightExpression),
)
} else {
target = ch.Factory().NewConditionalExpression(
createNotNullCondition(ch.EmitContext(), leftExpression, capturedLeft, true),
ch.Factory().NewToken(ast.KindQuestionToken),
ch.Factory().NewVoidZeroExpression(),
ch.Factory().NewToken(ast.KindColonToken),
rightExpression,
)
}
target.Loc = node.Loc
if thisArg != nil {
target = ch.Factory().NewSyntheticReferenceExpression(target, thisArg)
}
ch.EmitContext().SetOriginal(target, node.AsNode())
return target
}
func newOptionalChainTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &optionalChainTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,174 @@
package estransforms
import (
"strings"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
"github.com/microsoft/typescript-go/internal/transformers"
)
var newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n")
type taggedTemplateTransformer struct {
transformers.Transformer
currentSourceFile *ast.SourceFile
taggedTemplateStringDeclarations []*ast.Node
}
func newTaggedTemplateLiftRestrictionTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &taggedTemplateTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}
func (tx *taggedTemplateTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsInvalidTemplateEscape == 0 {
return node
}
switch node.Kind {
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
case ast.KindTaggedTemplateExpression:
return tx.visitTaggedTemplateExpression(node.AsTaggedTemplateExpression())
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *taggedTemplateTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
tx.currentSourceFile = node
tx.taggedTemplateStringDeclarations = nil
visited := tx.Visitor().VisitEachChild(node.AsNode())
if len(tx.taggedTemplateStringDeclarations) > 0 {
visitedSourceFile := visited.AsSourceFile()
statements := append(
visitedSourceFile.Statements.Nodes[:len(visitedSourceFile.Statements.Nodes):len(visitedSourceFile.Statements.Nodes)],
tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList(tx.taggedTemplateStringDeclarations),
ast.NodeFlagsNone,
),
),
)
stmtList := tx.Factory().NewNodeList(statements)
stmtList.Loc = node.Statements.Loc
visited = tx.Factory().UpdateSourceFile(visitedSourceFile, stmtList, visitedSourceFile.EndOfFileToken)
}
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
return visited
}
func (tx *taggedTemplateTransformer) visitTaggedTemplateExpression(node *ast.TaggedTemplateExpression) *ast.Node {
return tx.processTaggedTemplateExpression(node)
}
func (tx *taggedTemplateTransformer) processTaggedTemplateExpression(node *ast.TaggedTemplateExpression) *ast.Node {
tag := tx.Visitor().VisitNode(node.Tag)
template := node.Template
if !hasInvalidEscape(template) {
return tx.Visitor().VisitEachChild(node.AsNode())
}
f := tx.Factory()
// Build up the template arguments and the raw and cooked strings for the template.
templateArguments := []*ast.Node{nil} // placeholder for the template object
var cookedStrings []*ast.Node
var rawStrings []*ast.Node
if ast.IsNoSubstitutionTemplateLiteral(template) {
cookedStrings = append(cookedStrings, createTemplateCooked(f, template.TemplateLiteralLikeData()))
rawStrings = append(rawStrings, getRawLiteral(f, template))
} else {
te := template.AsTemplateExpression()
cookedStrings = append(cookedStrings, createTemplateCooked(f, te.Head.TemplateLiteralLikeData()))
rawStrings = append(rawStrings, getRawLiteral(f, te.Head))
for _, span := range te.TemplateSpans.Nodes {
ts := span.AsTemplateSpan()
cookedStrings = append(cookedStrings, createTemplateCooked(f, ts.Literal.TemplateLiteralLikeData()))
rawStrings = append(rawStrings, getRawLiteral(f, ts.Literal))
templateArguments = append(templateArguments, tx.Visitor().VisitNode(ts.Expression))
}
}
helperCall := f.NewTemplateObjectHelper(
f.NewArrayLiteralExpression(f.NewNodeList(cookedStrings), false),
f.NewArrayLiteralExpression(f.NewNodeList(rawStrings), false),
)
// Create a variable to cache the template object if we're in a module.
// Do not do this in the global scope, as any variable we currently generate could conflict with
// variables from outside of the current compilation. In the future, we can revisit this behavior.
if ast.IsExternalModule(tx.currentSourceFile) {
tempVar := f.NewUniqueName("templateObject")
tx.taggedTemplateStringDeclarations = append(
tx.taggedTemplateStringDeclarations,
f.NewVariableDeclaration(tempVar, nil, nil, nil),
)
templateArguments[0] = f.NewLogicalORExpression(
tempVar,
f.NewAssignmentExpression(tempVar, helperCall),
)
} else {
templateArguments[0] = helperCall
}
call := f.NewCallExpression(tag, nil /*questionDotToken*/, nil /*typeArguments*/, f.NewNodeList(templateArguments), ast.NodeFlagsNone)
call.Loc = node.Loc
return call
}
func createTemplateCooked(f *printer.NodeFactory, template *ast.TemplateLiteralLikeNodeBase) *ast.Node {
if template.TemplateFlags&ast.TokenFlagsIsInvalid != 0 {
return f.NewVoidZeroExpression()
}
return f.NewStringLiteral(template.Text, ast.TokenFlagsNone)
}
func getRawLiteral(f *printer.NodeFactory, node *ast.Node) *ast.Node {
text := node.TemplateLiteralLikeData().RawText
if text == "" {
text = scanner.GetSourceTextOfNodeFromSourceFile(ast.GetSourceFileOfNode(node), node, false /*includeTrivia*/)
// text contains the original source, it will also contain quotes ("`"), dollar signs and braces ("${" and "}"),
// thus we need to remove those characters.
// First template piece starts with "`", others with "}"
// Last template piece ends with "`", others with "${"
isLast := node.Kind == ast.KindNoSubstitutionTemplateLiteral || node.Kind == ast.KindTemplateTail
endLen := 2
if isLast {
endLen = 1
}
text = text[1 : len(text)-endLen]
}
// Newline normalization:
// ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV.
text = newlineNormalizer.Replace(text)
result := f.NewStringLiteral(text, ast.TokenFlagsNone)
result.Loc = node.Loc
return result
}
func hasInvalidEscape(template *ast.Node) bool {
if ast.IsNoSubstitutionTemplateLiteral(template) {
return template.TemplateLiteralLikeData().TemplateFlags&ast.TokenFlagsContainsInvalidEscape != 0
}
te := template.AsTemplateExpression()
if te.Head.TemplateLiteralLikeData().TemplateFlags&ast.TokenFlagsContainsInvalidEscape != 0 {
return true
}
for _, span := range te.TemplateSpans.Nodes {
if span.AsTemplateSpan().Literal.TemplateLiteralLikeData().TemplateFlags&ast.TokenFlagsContainsInvalidEscape != 0 {
return true
}
}
return false
}

View File

@@ -0,0 +1,50 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
func NewUseStrictTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &useStrictTransformer{
compilerOptions: opts.CompilerOptions,
getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile,
}
return tx.NewTransformer(tx.visit, opts.Context)
}
type useStrictTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
}
func (tx *useStrictTransformer) visit(node *ast.Node) *ast.Node {
if node.Kind != ast.KindSourceFile {
return node
}
return tx.visitSourceFile(node.AsSourceFile())
}
func (tx *useStrictTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.ScriptKind == core.ScriptKindJSON {
return node.AsNode()
}
isExternalModule := ast.IsExternalModule(node)
moduleKind := tx.compilerOptions.GetEmitModuleKind()
format := tx.getEmitModuleFormatOfFile(node)
// ESM is always strict. If the file is ESM, and CJS emit
// has not been requested, then skip adding "use strict".
if isExternalModule && moduleKind >= core.ModuleKindES2015 &&
(moduleKind == core.ModuleKindPreserve || format >= core.ModuleKindES2015) {
return node.AsNode()
}
statements := tx.Factory().EnsureUseStrict(node.Statements.Nodes)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Statements.Loc
return tx.Factory().UpdateSourceFile(node, statementList, node.EndOfFileToken).AsSourceFile().AsNode()
}

View File

@@ -0,0 +1,799 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type usingDeclarationTransformer struct {
transformers.Transformer
exportBindings map[string]*ast.ExportSpecifierNode
exportBindingNames []string
exportVars []*ast.VariableDeclarationNode
defaultExportBinding *ast.IdentifierNode
exportEqualsBinding *ast.IdentifierNode
}
func newUsingDeclarationTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &usingDeclarationTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}
type usingKind uint
const (
usingKindNone usingKind = iota
usingKindSync
usingKindAsync
)
func (tx *usingDeclarationTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsUsing == 0 {
return node
}
switch node.Kind {
case ast.KindSourceFile:
node = tx.visitSourceFile(node.AsSourceFile())
case ast.KindBlock:
node = tx.visitBlock(node.AsBlock())
case ast.KindForStatement:
node = tx.visitForStatement(node.AsForStatement())
case ast.KindForOfStatement:
node = tx.visitForOfStatement(node.AsForInOrOfStatement())
default:
node = tx.Visitor().VisitEachChild(node)
}
return node
}
func (tx *usingDeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
var visited *ast.SourceFileNode
usingKind := getUsingKindOfStatements(node.Statements.Nodes)
if usingKind != usingKindNone {
// Imports and exports must stay at the top level. This means we must hoist all imports, exports, and
// top-level function declarations and bindings out of the `try` statements we generate. For example:
//
// given:
//
// import { w } from "mod";
// const x = expr1;
// using y = expr2;
// const z = expr3;
// export function f() {
// console.log(z);
// }
//
// produces:
//
// import { x } from "mod"; // <-- preserved
// const x = expr1; // <-- preserved
// var y, z; // <-- hoisted
// export function f() { // <-- hoisted
// console.log(z);
// }
// const env_1 = { stack: [], error: void 0, hasError: false };
// try {
// y = __addDisposableResource(env_1, expr2, false);
// z = expr3;
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResource(env_1);
// }
//
// In this transformation, we hoist `y`, `z`, and `f` to a new outer statement list while moving all other
// statements in the source file into the `try` block, which is the same approach we use for System module
// emit. Unlike System module emit, we attempt to preserve all statements prior to the first top-level
// `using` to isolate the complexity of the transformed output to only where it is necessary.
tx.EmitContext().StartVariableEnvironment()
tx.exportBindings = make(map[string]*ast.ExportSpecifierNode)
tx.exportVars = nil
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
var topLevelStatements []*ast.Statement
topLevelStatements = append(topLevelStatements, core.FirstResult(tx.Visitor().VisitSlice(prologue))...)
// Collect and transform any leading statements up to the first `using` or `await using`. This preserves
// the original statement order much as is possible.
pos := 0
for pos < len(rest) {
statement := rest[pos]
if getUsingKind(statement) != usingKindNone {
if pos > 0 {
topLevelStatements = append(topLevelStatements, core.FirstResult(tx.Visitor().VisitSlice(rest[:pos]))...)
}
break
}
pos++
}
if pos >= len(rest) {
panic("Should have encountered at least one 'using' statement.")
}
// transform the rest of the body
envBinding := tx.createEnvBinding()
bodyStatements := tx.transformUsingDeclarations(rest[pos:], envBinding, &topLevelStatements)
// add `export {}` declarations for any hoisted bindings.
if len(tx.exportBindings) > 0 {
exportSpecifiers := make([]*ast.ExportSpecifierNode, 0, len(tx.exportBindingNames))
for _, name := range tx.exportBindingNames {
specifier := tx.exportBindings[name]
debug.Assert(specifier != nil, "Missing export binding for hoisted export name")
exportSpecifiers = append(exportSpecifiers, specifier)
}
topLevelStatements = append(
topLevelStatements,
tx.Factory().NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
tx.Factory().NewNamedExports(
tx.Factory().NewNodeList(
exportSpecifiers,
),
),
nil, /*moduleSpecifier*/
nil, /*attributes*/
),
)
}
topLevelStatements = append(topLevelStatements, tx.EmitContext().EndVariableEnvironment()...)
if len(tx.exportVars) > 0 {
topLevelStatements = append(topLevelStatements, tx.Factory().NewVariableStatement(
tx.Factory().NewModifierList([]*ast.Node{
tx.Factory().NewModifier(ast.KindExportKeyword),
}),
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList(tx.exportVars),
ast.NodeFlagsLet,
),
))
}
topLevelStatements = append(topLevelStatements, tx.createDownlevelUsingStatements(bodyStatements, envBinding, usingKind == usingKindAsync)...)
if tx.exportEqualsBinding != nil {
topLevelStatements = append(topLevelStatements, tx.Factory().NewExportAssignment(
nil, /*modifiers*/
true, /*isExportEquals*/
nil, /*typeNode*/
tx.exportEqualsBinding,
))
}
visited = tx.Factory().UpdateSourceFile(node, tx.Factory().NewNodeList(topLevelStatements), node.EndOfFileToken)
} else {
visited = tx.Visitor().VisitEachChild(node.AsNode())
}
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
tx.exportVars = nil
tx.exportBindings = nil
tx.exportBindingNames = nil
tx.defaultExportBinding = nil
tx.exportEqualsBinding = nil
return visited
}
func (tx *usingDeclarationTransformer) visitBlock(node *ast.Block) *ast.Node {
usingKind := getUsingKindOfStatements(node.Statements.Nodes)
if usingKind != usingKindNone {
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
envBinding := tx.createEnvBinding()
statements := make([]*ast.Statement, 0, len(prologue)+2)
statements = append(statements, core.FirstResult(tx.Visitor().VisitSlice(prologue))...)
statements = append(statements, tx.createDownlevelUsingStatements(
tx.transformUsingDeclarations(rest, envBinding, nil /*topLevelStatements*/),
envBinding,
usingKind == usingKindAsync,
)...)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Statements.Loc
return tx.Factory().UpdateBlock(node, statementList, node.MultiLine)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *usingDeclarationTransformer) visitForStatement(node *ast.ForStatement) *ast.Node {
if node.Initializer != nil && isUsingVariableDeclarationList(node.Initializer) {
// given:
//
// for (using x = expr; cond; incr) { ... }
//
// produces a shallow transformation to:
//
// {
// using x = expr;
// for (; cond; incr) { ... }
// }
//
// before handing the shallow transformation back to the visitor for an in-depth transformation.
return tx.Visitor().VisitNode(
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewVariableStatement(nil /*modifiers*/, node.Initializer),
tx.Factory().UpdateForStatement(
node,
nil, /*initializer*/
node.Condition,
node.Incrementor,
node.Statement,
),
}), false /*multiLine*/),
)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *usingDeclarationTransformer) visitForOfStatement(node *ast.ForInOrOfStatement) *ast.Node {
if isUsingVariableDeclarationList(node.Initializer) {
// given:
//
// for (using x of y) { ... }
//
// produces a shallow transformation to:
//
// for (const x_1 of y) {
// using x = x;
// ...
// }
//
// before handing the shallow transformation back to the visitor for an in-depth transformation.
forInitializer := node.Initializer.AsVariableDeclarationList()
forDecl := core.FirstOrNil(forInitializer.Declarations.Nodes)
if forDecl == nil {
forDecl = tx.Factory().NewVariableDeclaration(tx.Factory().NewTempVariable(), nil, nil, nil)
}
isAwaitUsing := getUsingKindOfVariableDeclarationList(forInitializer) == usingKindAsync
temp := tx.Factory().NewGeneratedNameForNode(forDecl.Name())
usingVar := tx.Factory().UpdateVariableDeclaration(forDecl.AsVariableDeclaration(), forDecl.Name(), nil /*exclamationToken*/, nil /*type*/, temp)
usingVarList := tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.Node{usingVar}),
core.IfElse(isAwaitUsing, ast.NodeFlagsAwaitUsing, ast.NodeFlagsUsing),
)
usingVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, usingVarList)
var statement *ast.Statement
if ast.IsBlock(node.Statement) {
statements := make([]*ast.Statement, 0, len(node.Statement.Statements())+1)
statements = append(statements, usingVarStatement)
statements = append(statements, node.Statement.Statements()...)
statement = tx.Factory().UpdateBlock(
node.Statement.AsBlock(),
tx.Factory().NewNodeList(statements),
node.Statement.AsBlock().MultiLine,
)
} else {
statement = tx.Factory().NewBlock(
tx.Factory().NewNodeList([]*ast.Statement{
usingVarStatement,
node.Statement,
}),
true, /*multiLine*/
)
}
return tx.Visitor().VisitNode(
tx.Factory().UpdateForInOrOfStatement(
node,
node.AwaitModifier,
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
tx.Factory().NewVariableDeclaration(temp, nil /*exclamationToken*/, nil /*type*/, nil),
}),
ast.NodeFlagsConst,
),
node.Expression,
statement,
),
)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *usingDeclarationTransformer) transformUsingDeclarations(statementsIn []*ast.Statement, envBinding *ast.IdentifierNode, topLevelStatements *[]*ast.Statement) []*ast.Node {
var statements []*ast.Statement
hoist := func(node *ast.Statement) *ast.Statement {
if topLevelStatements == nil {
return node
}
switch node.Kind {
case ast.KindImportDeclaration,
ast.KindImportEqualsDeclaration,
ast.KindExportDeclaration,
ast.KindFunctionDeclaration:
tx.hoistImportOrExportOrHoistedDeclaration(node, topLevelStatements)
return nil
case ast.KindExportAssignment:
return tx.hoistExportAssignment(node.AsExportAssignment())
case ast.KindClassDeclaration:
return tx.hoistClassDeclaration(node.AsClassDeclaration())
case ast.KindVariableStatement:
return tx.hoistVariableStatement(node.AsVariableStatement())
}
return node
}
hoistOrAppendNode := func(node *ast.Node) {
node = hoist(node)
if node != nil {
statements = append(statements, node)
}
}
for _, statement := range statementsIn {
usingKind := getUsingKind(statement)
if usingKind != usingKindNone {
varStatement := statement.AsVariableStatement()
declarationList := varStatement.DeclarationList
var declarations []*ast.VariableDeclarationNode
for _, declaration := range declarationList.AsVariableDeclarationList().Declarations.Nodes {
if !ast.IsIdentifier(declaration.Name()) {
// Since binding patterns are a grammar error, we reset `declarations` so we don't process this as a `using`.
declarations = nil
break
}
// perform a shallow transform for any named evaluation
if isNamedEvaluation(tx.EmitContext(), declaration) {
declaration = transformNamedEvaluation(tx.EmitContext(), declaration, false /*ignoreEmptyStringLiteral*/, "" /*assignedName*/)
}
initializer := tx.Visitor().VisitNode(declaration.Initializer())
if initializer == nil {
initializer = tx.Factory().NewVoidZeroExpression()
}
declarations = append(declarations, tx.Factory().UpdateVariableDeclaration(
declaration.AsVariableDeclaration(),
declaration.Name(),
nil, /*exclamationToken*/
nil, /*type*/
tx.Factory().NewAddDisposableResourceHelper(
envBinding,
initializer,
usingKind == usingKindAsync,
),
))
}
// Only replace the statement if it was valid.
if len(declarations) > 0 {
varList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList(declarations), ast.NodeFlagsConst)
tx.EmitContext().SetOriginal(varList, declarationList)
varList.Loc = declarationList.Loc
hoistOrAppendNode(tx.Factory().UpdateVariableStatement(varStatement, nil /*modifiers*/, varList))
continue
}
}
if result := tx.visit(statement); result != nil {
if result.Kind == ast.KindSyntaxList {
for _, node := range result.AsSyntaxList().Children {
hoistOrAppendNode(node)
}
} else {
hoistOrAppendNode(result)
}
}
}
return statements
}
func (tx *usingDeclarationTransformer) hoistImportOrExportOrHoistedDeclaration(node *ast.Statement, topLevelStatements *[]*ast.Statement) {
// NOTE: `node` has already been visited
*topLevelStatements = append(*topLevelStatements, node)
}
func (tx *usingDeclarationTransformer) hoistExportAssignment(node *ast.ExportAssignment) *ast.Statement {
if node.IsExportEquals {
return tx.hoistExportEquals(node)
} else {
return tx.hoistExportDefault(node)
}
}
func (tx *usingDeclarationTransformer) hoistExportDefault(node *ast.ExportAssignment) *ast.Statement {
// NOTE: `node` has already been visited
if tx.defaultExportBinding != nil {
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
return node.AsNode()
}
// given:
//
// export default expr;
//
// produces:
//
// // top level
// var default_1;
// export { default_1 as default };
//
// // body
// default_1 = expr;
tx.defaultExportBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
tx.hoistBindingIdentifier(tx.defaultExportBinding /*isExport*/, true, tx.Factory().NewIdentifier("default"), node.AsNode())
// give a class or function expression an assigned name, if needed.
expression := node.Expression
innerExpression := ast.SkipOuterExpressions(expression, ast.OEKAll)
if isNamedEvaluation(tx.EmitContext(), innerExpression) {
innerExpression = transformNamedEvaluation(tx.EmitContext(), innerExpression /*ignoreEmptyStringLiteral*/, false, "default")
expression = tx.Factory().RestoreOuterExpressions(expression, innerExpression, ast.OEKAll)
}
assignment := tx.Factory().NewAssignmentExpression(tx.defaultExportBinding, expression)
return tx.Factory().NewExpressionStatement(assignment)
}
func (tx *usingDeclarationTransformer) hoistExportEquals(node *ast.ExportAssignment) *ast.Statement {
// NOTE: `node` has already been visited
if tx.exportEqualsBinding != nil {
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
return node.AsNode()
}
// given:
//
// export = expr;
//
// produces:
//
// // top level
// var default_1;
//
// try {
// // body
// default_1 = expr;
// } ...
//
// // top level suffix
// export = default_1;
tx.exportEqualsBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
tx.EmitContext().AddVariableDeclaration(tx.exportEqualsBinding)
// give a class or function expression an assigned name, if needed.
assignment := tx.Factory().NewAssignmentExpression(tx.exportEqualsBinding, node.Expression)
return tx.Factory().NewExpressionStatement(assignment)
}
func (tx *usingDeclarationTransformer) hoistClassDeclaration(node *ast.ClassDeclaration) *ast.Statement {
// NOTE: `node` has already been visited
if node.Name() == nil && tx.defaultExportBinding != nil {
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
return node.AsNode()
}
isExported := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport)
isDefault := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsDefault)
// When hoisting a class declaration at the top level of a file containing a top-level `using` statement, we
// must first convert it to a class expression so that we can hoist the binding outside of the `try`.
expression := convertClassDeclarationToClassExpression(tx.EmitContext(), node)
if node.Name() != nil {
// given:
//
// using x = expr;
// class C {}
//
// produces:
//
// var x, C;
// const env_1 = { ... };
// try {
// x = __addDisposableResource(env_1, expr, false);
// C = class {};
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
//
// If the class is exported, we also produce an `export { C };`
tx.hoistBindingIdentifier(tx.Factory().GetLocalName(node.AsNode()), isExported && !isDefault, nil /*exportAlias*/, node.AsNode())
expression = tx.Factory().NewAssignmentExpression(tx.Factory().GetDeclarationName(node.AsNode()), expression)
tx.EmitContext().SetOriginal(expression, node.AsNode())
tx.EmitContext().SetSourceMapRange(expression, node.Loc)
tx.EmitContext().SetCommentRange(expression, node.Loc)
if isNamedEvaluation(tx.EmitContext(), expression) {
expression = transformNamedEvaluation(tx.EmitContext(), expression, false /*ignoreEmptyStringLiteral*/, "" /*assignedName*/)
}
}
if isDefault && tx.defaultExportBinding == nil {
// In the case of a default export, we create a temporary variable that we export as the default and then
// assign to that variable.
//
// given:
//
// using x = expr;
// export default class C {}
//
// produces:
//
// export { default_1 as default };
// var x, C, default_1;
// const env_1 = { ... };
// try {
// x = __addDisposableResource(env_1, expr, false);
// default_1 = C = class {};
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
//
// Though we will never reassign `default_1`, this most closely matches the specified runtime semantics.
tx.defaultExportBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
tx.hoistBindingIdentifier(tx.defaultExportBinding /*isExport*/, true, tx.Factory().NewIdentifier("default"), node.AsNode())
expression = tx.Factory().NewAssignmentExpression(tx.defaultExportBinding, expression)
tx.EmitContext().SetOriginal(expression, node.AsNode())
if isNamedEvaluation(tx.EmitContext(), expression) {
expression = transformNamedEvaluation(tx.EmitContext(), expression /*ignoreEmptyStringLiteral*/, false, "default")
}
}
return tx.Factory().NewExpressionStatement(expression)
}
func (tx *usingDeclarationTransformer) hoistVariableStatement(node *ast.VariableStatement) *ast.Statement {
// NOTE: `node` has already been visited
var expressions []*ast.Expression
isExported := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport)
for _, variable := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
tx.hoistBindingElement(variable, isExported, variable)
if variable.Initializer() != nil {
expressions = append(expressions, tx.hoistInitializedVariable(variable.AsVariableDeclaration()))
}
}
if len(expressions) > 0 {
statement := tx.Factory().NewExpressionStatement(tx.Factory().InlineExpressions(expressions))
tx.EmitContext().SetOriginal(statement, node.AsNode())
tx.EmitContext().SetCommentRange(statement, node.Loc)
tx.EmitContext().SetSourceMapRange(statement, node.Loc)
return statement
}
return nil
}
func (tx *usingDeclarationTransformer) hoistInitializedVariable(node *ast.VariableDeclaration) *ast.Expression {
// NOTE: `node` has already been visited
if node.Initializer == nil {
panic("Expected initializer")
}
var target *ast.Expression
if ast.IsIdentifier(node.Name()) {
target = node.Name().Clone(tx.Factory())
tx.EmitContext().SetEmitFlags(target, tx.EmitContext().EmitFlags(target) & ^(printer.EFLocalName|printer.EFExportName))
} else {
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), node.Name().AsBindingPattern())
}
assignment := tx.Factory().NewAssignmentExpression(target, node.Initializer)
tx.EmitContext().SetOriginal(assignment, node.AsNode())
tx.EmitContext().SetCommentRange(assignment, node.Loc)
tx.EmitContext().SetSourceMapRange(assignment, node.Loc)
return assignment
}
func (tx *usingDeclarationTransformer) hoistBindingElement(node *ast.Node /*VariableDeclaration|BindingElement*/, isExportedDeclaration bool, original *ast.Node) {
// NOTE: `node` has already been visited
if ast.IsBindingPattern(node.Name()) {
for _, element := range node.Name().Elements() {
if element.Name() != nil {
tx.hoistBindingElement(element, isExportedDeclaration, original)
}
}
} else {
tx.hoistBindingIdentifier(node.Name(), isExportedDeclaration, nil /*exportAlias*/, original)
}
}
func (tx *usingDeclarationTransformer) hoistBindingIdentifier(node *ast.IdentifierNode, isExport bool, exportAlias *ast.IdentifierNode, original *ast.Node) {
// NOTE: `node` has already been visited
name := node
if !transformers.IsGeneratedIdentifier(tx.EmitContext(), node) {
name = name.Clone(tx.Factory())
}
if isExport {
if exportAlias == nil && !transformers.IsLocalName(tx.EmitContext(), name) {
varDecl := tx.Factory().NewVariableDeclaration(name, nil /*exclamationToken*/, nil /*type*/, nil /*initializer*/)
if original != nil {
tx.EmitContext().SetOriginal(varDecl, original)
}
tx.exportVars = append(tx.exportVars, varDecl)
return
}
var localName *ast.ModuleExportName
var exportName *ast.ModuleExportName
if exportAlias != nil {
localName = name
exportName = exportAlias
} else {
exportName = name
}
specifier := tx.Factory().NewExportSpecifier( /*isTypeOnly*/ false, localName, exportName)
if original != nil {
tx.EmitContext().SetOriginal(specifier, original)
}
if tx.exportBindings == nil {
tx.exportBindings = make(map[string]*ast.ExportSpecifierNode)
}
if _, ok := tx.exportBindings[name.Text()]; !ok {
tx.exportBindingNames = append(tx.exportBindingNames, name.Text())
}
tx.exportBindings[name.Text()] = specifier
}
tx.EmitContext().AddVariableDeclaration(name)
}
func (tx *usingDeclarationTransformer) createEnvBinding() *ast.IdentifierNode {
return tx.Factory().NewUniqueName("env")
}
func (tx *usingDeclarationTransformer) createDownlevelUsingStatements(bodyStatements []*ast.Node, envBinding *ast.IdentifierNode, async bool) []*ast.Statement {
statements := make([]*ast.Statement, 0, 2)
// produces:
//
// const env_1 = { stack: [], error: void 0, hasError: false };
//
envObject := tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Expression{
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("stack"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewArrayLiteralExpression(nil, false /*multiLine*/)),
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("error"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewVoidZeroExpression()),
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("hasError"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewFalseExpression()),
}), false /*multiLine*/)
envVar := tx.Factory().NewVariableDeclaration(envBinding, nil /*exclamationToken*/, nil /*typeNode*/, envObject)
envVarList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{envVar}), ast.NodeFlagsConst)
envVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, envVarList)
statements = append(statements, envVarStatement)
// when `async` is `false`, produces:
//
// try {
// <bodyStatements>
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
// when `async` is `true`, produces:
//
// try {
// <bodyStatements>
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// const result_1 = __disposeResources(env_1);
// if (result_1) {
// await result_1;
// }
// }
// Unfortunately, it is necessary to use two properties to indicate an error because `throw undefined` is legal
// JavaScript.
tryBlock := tx.Factory().NewBlock(tx.Factory().NewNodeList(bodyStatements), true /*multiLine*/)
bodyCatchBinding := tx.Factory().NewUniqueName("e")
catchClause := tx.Factory().NewCatchClause(
tx.Factory().NewVariableDeclaration(
bodyCatchBinding,
nil, /*exclamationToken*/
nil, /*type*/
nil, /*initializer*/
),
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("error"), ast.NodeFlagsNone),
bodyCatchBinding,
),
),
tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("hasError"), ast.NodeFlagsNone),
tx.Factory().NewTrueExpression(),
),
),
}), true /*multiLine*/),
)
var finallyBlock *ast.BlockNode
if async {
result := tx.Factory().NewUniqueName("result")
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
tx.Factory().NewVariableDeclaration(
result,
nil, /*exclamationToken*/
nil, /*type*/
tx.Factory().NewDisposeResourcesHelper(envBinding),
),
}), ast.NodeFlagsConst),
),
tx.Factory().NewIfStatement(result, tx.Factory().NewExpressionStatement(tx.Factory().NewAwaitExpression(result)), nil /*elseStatement*/),
}), true /*multiLine*/)
} else {
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewExpressionStatement(
tx.Factory().NewDisposeResourcesHelper(envBinding),
),
}), true /*multiLine*/)
}
tryStatement := tx.Factory().NewTryStatement(tryBlock, catchClause, finallyBlock)
statements = append(statements, tryStatement)
return statements
}
func isUsingVariableDeclarationList(node *ast.ForInitializer) bool {
return ast.IsVariableDeclarationList(node) && getUsingKindOfVariableDeclarationList(node.AsVariableDeclarationList()) != usingKindNone
}
func getUsingKindOfVariableDeclarationList(node *ast.VariableDeclarationList) usingKind {
switch node.Flags & ast.NodeFlagsBlockScoped {
case ast.NodeFlagsAwaitUsing:
return usingKindAsync
case ast.NodeFlagsUsing:
return usingKindSync
default:
return usingKindNone
}
}
func getUsingKindOfVariableStatement(node *ast.VariableStatement) usingKind {
return getUsingKindOfVariableDeclarationList(node.DeclarationList.AsVariableDeclarationList())
}
func getUsingKind(statement *ast.Node) usingKind {
if ast.IsVariableStatement(statement) {
return getUsingKindOfVariableStatement(statement.AsVariableStatement())
}
return usingKindNone
}
func getUsingKindOfStatements(statements []*ast.Node) usingKind {
result := usingKindNone
for _, statement := range statements {
usingKind := getUsingKind(statement)
if usingKind == usingKindAsync {
return usingKindAsync
}
if usingKind > result {
result = usingKind
}
}
return result
}

View File

@@ -0,0 +1,289 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
func convertClassDeclarationToClassExpression(emitContext *printer.EmitContext, node *ast.ClassDeclaration) *ast.Expression {
updated := emitContext.Factory.NewClassExpression(
transformers.ExtractModifiers(emitContext, node.Modifiers(), ^ast.ModifierFlagsExportDefault),
node.Name(),
node.TypeParameters,
node.HeritageClauses,
node.Members,
)
emitContext.SetOriginal(updated, node.AsNode())
updated.Loc = node.Loc
return updated
}
func createNotNullCondition(emitContext *printer.EmitContext, left *ast.Node, right *ast.Node, invert bool) *ast.Node {
token := ast.KindExclamationEqualsEqualsToken
op := ast.KindAmpersandAmpersandToken
if invert {
token = ast.KindEqualsEqualsEqualsToken
op = ast.KindBarBarToken
}
return emitContext.Factory.NewBinaryExpression(
nil,
emitContext.Factory.NewBinaryExpression(
nil,
left,
nil,
emitContext.Factory.NewToken(token),
emitContext.Factory.NewKeywordExpression(ast.KindNullKeyword),
),
nil,
emitContext.Factory.NewToken(op),
emitContext.Factory.NewBinaryExpression(
nil,
right,
nil,
emitContext.Factory.NewToken(token),
emitContext.Factory.NewVoidZeroExpression(),
),
)
}
// superAccessState tracks super property/element accesses and super property assignments
// within async function or async generator bodies. It is embedded by both asyncTransformer
// and forawaitTransformer to share the tracking logic.
type superAccessState struct {
factory *printer.NodeFactory
// Keeps track of property names accessed on super (`super.x`) within async functions.
capturedSuperProperties *collections.OrderedSet[string]
// Whether the async function contains an element access on super (`super[x]`).
hasSuperElementAccess bool
hasSuperPropertyAssignment bool
superBinding *ast.IdentifierNode
superIndexBinding *ast.IdentifierNode
superAccessVisitor *ast.NodeVisitor
}
func (s *superAccessState) initSuperAccessVisitor(emitContext *printer.EmitContext, factory *printer.NodeFactory) {
s.factory = factory
s.superAccessVisitor = emitContext.NewNodeVisitor(s.visitSuperAccessNode)
}
// visitSuperAccessNode walks the async/generator body and replaces super property/element
// accesses with _super/_superIndex references. This is necessary because the async body
// ends up inside a generator function where `super` is not valid.
func (s *superAccessState) visitSuperAccessNode(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindCallExpression:
call := node.AsCallExpression()
if ast.IsSuperProperty(call.Expression) {
return s.substituteCallExpressionWithSuperAccess(call, s.superAccessVisitor)
}
return s.superAccessVisitor.VisitEachChild(node)
case ast.KindPropertyAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
// super.x → _super.x
return s.factory.NewPropertyAccessExpression(
s.superBinding, nil, node.Name(), ast.NodeFlagsNone,
)
}
return s.superAccessVisitor.VisitEachChild(node)
case ast.KindElementAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
// super[x] → _superIndex(x) or _superIndex(x).value
return s.createSuperElementAccessInAsyncMethod(
node.AsElementAccessExpression().ArgumentExpression,
)
}
return s.superAccessVisitor.VisitEachChild(node)
// Don't recurse into non-arrow function scopes or classes
case ast.KindFunctionExpression, ast.KindFunctionDeclaration,
ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor,
ast.KindConstructor, ast.KindClassDeclaration, ast.KindClassExpression:
return node
default:
return s.superAccessVisitor.VisitEachChild(node)
}
}
func (s *superAccessState) substituteSuperAccessesInBody(body *ast.Node) *ast.Node {
return s.superAccessVisitor.VisitNode(body)
}
// substituteCallExpressionWithSuperAccess handles super.x(args) and super[x](args).
func (s *superAccessState) substituteCallExpressionWithSuperAccess(call *ast.CallExpression, visitor *ast.NodeVisitor) *ast.Node {
expression := call.Expression
var target *ast.Node
if ast.IsPropertyAccessExpression(expression) {
// super.x(args) → _super.x.call(this, args)
target = s.factory.NewPropertyAccessExpression(
s.superBinding, nil,
expression.AsPropertyAccessExpression().Name(), ast.NodeFlagsNone,
)
} else if ast.IsElementAccessExpression(expression) {
// super[x](args) → _superIndex(x).call(this, args) or _superIndex(x).value.call(this, args)
target = s.createSuperElementAccessInAsyncMethod(
expression.AsElementAccessExpression().ArgumentExpression,
)
} else {
return visitor.VisitEachChild(call.AsNode())
}
callTarget := s.factory.NewPropertyAccessExpression(
target, nil,
s.factory.NewIdentifier("call"), ast.NodeFlagsNone,
)
var allArgs []*ast.Node
allArgs = append(allArgs, s.factory.NewThisExpression())
if call.Arguments != nil {
visitedArgs := visitor.VisitNodes(call.Arguments)
if visitedArgs != nil {
allArgs = append(allArgs, visitedArgs.Nodes...)
}
}
result := s.factory.NewCallExpression(
callTarget, nil, nil,
s.factory.NewNodeList(allArgs), ast.NodeFlagsNone,
)
result.Loc = call.Loc
return result
}
// createSuperElementAccessInAsyncMethod creates _superIndex(x) or _superIndex(x).value.
func (s *superAccessState) createSuperElementAccessInAsyncMethod(argumentExpression *ast.Node) *ast.Node {
superIndexCall := s.factory.NewCallExpression(
s.superIndexBinding, nil, nil,
s.factory.NewNodeList([]*ast.Node{argumentExpression}),
ast.NodeFlagsNone,
)
if s.hasSuperPropertyAssignment {
return s.factory.NewPropertyAccessExpression(
superIndexCall, nil,
s.factory.NewIdentifier("value"), ast.NodeFlagsNone,
)
}
return superIndexCall
}
// createSuperAccessVariableStatement creates a variable named `_super` with accessor
// properties for the given property names.
//
// Create a variable declaration with a getter/setter (if binding) definition for each name:
//
// const _super = Object.create(null, {
// x: { get: () => super.x }, // read-only
// x: { get: () => super.x, set: (v) => super.x = v }, // read-write
// });
func (s *superAccessState) createSuperAccessVariableStatement() *ast.Node {
f := s.factory
var accessors []*ast.Node
for name := range s.capturedSuperProperties.Values() {
var descriptorProperties []*ast.Node
// getter: get: () => super.name
getterBody := f.NewPropertyAccessExpression(
f.NewKeywordExpression(ast.KindSuperKeyword), nil,
f.NewIdentifier(name), ast.NodeFlagsNone,
)
getterArrow := f.NewArrowFunction(
nil, nil,
f.NewNodeList([]*ast.Node{}),
nil, nil,
f.NewToken(ast.KindEqualsGreaterThanToken),
getterBody,
)
getter := f.NewPropertyAssignment(nil, f.NewIdentifier("get"), nil, nil, getterArrow)
descriptorProperties = append(descriptorProperties, getter)
if s.hasSuperPropertyAssignment {
// setter: set: v => super.name = v
vParam := f.NewParameterDeclaration(nil, nil, f.NewIdentifier("v"), nil, nil, nil)
superProp := f.NewPropertyAccessExpression(
f.NewKeywordExpression(ast.KindSuperKeyword), nil,
f.NewIdentifier(name), ast.NodeFlagsNone,
)
assignExpr := f.NewAssignmentExpression(superProp, f.NewIdentifier("v"))
setterArrow := f.NewArrowFunction(
nil, nil,
f.NewNodeList([]*ast.Node{vParam}),
nil, nil,
f.NewToken(ast.KindEqualsGreaterThanToken),
assignExpr,
)
setter := f.NewPropertyAssignment(nil, f.NewIdentifier("set"), nil, nil, setterArrow)
descriptorProperties = append(descriptorProperties, setter)
}
descriptor := f.NewObjectLiteralExpression(f.NewNodeList(descriptorProperties), false)
accessor := f.NewPropertyAssignment(nil, f.NewIdentifier(name), nil, nil, descriptor)
accessors = append(accessors, accessor)
}
descriptorsObject := f.NewObjectLiteralExpression(f.NewNodeList(accessors), true)
objectCreateCall := f.NewCallExpression(
f.NewPropertyAccessExpression(
f.NewIdentifier("Object"), nil,
f.NewIdentifier("create"), ast.NodeFlagsNone,
), nil, nil,
f.NewNodeList([]*ast.Node{
f.NewKeywordExpression(ast.KindNullKeyword),
descriptorsObject,
}),
ast.NodeFlagsNone,
)
decl := f.NewVariableDeclaration(s.superBinding, nil, nil, objectCreateCall)
declList := f.NewVariableDeclarationList(f.NewNodeList([]*ast.Node{decl}), ast.NodeFlagsConst)
return f.NewVariableStatement(nil, declList)
}
// trackSuperAccess records super property/element accesses and super property assignments
// for the enclosing async method body. Called from both the main visitor and auxiliary
// visitors to ensure super accesses are tracked regardless of whether the node has
// transform flags.
func (s *superAccessState) trackSuperAccess(node *ast.Node) {
if s.capturedSuperProperties == nil {
return
}
switch node.Kind {
case ast.KindPropertyAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
s.capturedSuperProperties.Add(node.Name().Text())
}
case ast.KindElementAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
s.hasSuperElementAccess = true
}
case ast.KindBinaryExpression:
if ast.IsAssignmentOperator(node.AsBinaryExpression().OperatorToken.Kind) && assignmentTargetContainsSuperProperty(node.AsBinaryExpression().Left) {
s.hasSuperPropertyAssignment = true
}
case ast.KindPrefixUnaryExpression:
if isUpdateExpression(node) && assignmentTargetContainsSuperProperty(node.AsPrefixUnaryExpression().Operand) {
s.hasSuperPropertyAssignment = true
}
case ast.KindPostfixUnaryExpression:
if isUpdateExpression(node) && assignmentTargetContainsSuperProperty(node.AsPostfixUnaryExpression().Operand) {
s.hasSuperPropertyAssignment = true
}
}
}
// createAccessorPropertyBackingField creates a private backing field for an `accessor` PropertyDeclaration.
func createAccessorPropertyBackingField(f *printer.NodeFactory, node *ast.PropertyDeclaration, modifiers *ast.ModifierList, initializer *ast.Expression) *ast.Node {
return f.UpdatePropertyDeclaration(
node,
modifiers,
f.NewGeneratedPrivateNameForNodeEx(node.Name(), printer.AutoGenerateOptions{Suffix: "_accessor_storage"}),
nil, /*postfixToken*/
nil, /*typeNode*/
initializer,
)
}

View File

@@ -0,0 +1,102 @@
package inliners
import (
"strings"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/jsnum"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
"github.com/microsoft/typescript-go/internal/transformers"
)
type ConstEnumInliningTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
currentSourceFile *ast.SourceFile
emitResolver printer.EmitResolver
}
func NewConstEnumInliningTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opt.CompilerOptions
emitContext := opt.Context
if compilerOptions.GetIsolatedModules() {
debug.Fail("const enums are not inlined under isolated modules")
}
tx := &ConstEnumInliningTransformer{compilerOptions: compilerOptions, emitResolver: opt.EmitResolver}
return tx.NewTransformer(tx.visit, emitContext)
}
func (tx *ConstEnumInliningTransformer) visit(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
{
parse := tx.EmitContext().ParseNode(node)
if parse == nil {
return tx.Visitor().VisitEachChild(node)
}
value := tx.emitResolver.GetConstantValue(parse)
if value != nil {
var replacement *ast.Node
switch v := value.(type) {
case jsnum.Number:
if v.IsInf() {
if v.Abs() == v {
replacement = tx.Factory().NewIdentifier("Infinity")
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewIdentifier("Infinity"))
}
} else if v.IsNaN() {
replacement = tx.Factory().NewIdentifier("NaN")
} else if v.Abs() == v {
replacement = tx.Factory().NewNumericLiteral(v.String(), ast.TokenFlagsNone)
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewNumericLiteral(v.Abs().String(), ast.TokenFlagsNone))
}
case string:
replacement = tx.Factory().NewStringLiteral(v, ast.TokenFlagsNone)
case jsnum.PseudoBigInt: // technically not supported by strada, and issues a checker error, handled here for completeness
if v == (jsnum.PseudoBigInt{}) {
replacement = tx.Factory().NewBigIntLiteral("0", ast.TokenFlagsNone)
} else if !v.Negative {
replacement = tx.Factory().NewBigIntLiteral(v.Base10Value, ast.TokenFlagsNone)
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewBigIntLiteral(v.Base10Value, ast.TokenFlagsNone))
}
}
if tx.compilerOptions.RemoveComments.IsFalseOrUnknown() {
original := tx.EmitContext().MostOriginal(node)
if original != nil && !ast.NodeIsSynthesized(original) {
originalText := scanner.GetTextOfNode(original)
escapedText := safeMultiLineComment(originalText)
tx.EmitContext().AddSyntheticTrailingComment(replacement, ast.KindMultiLineCommentTrivia, escapedText, false)
}
}
return replacement
}
return tx.Visitor().VisitEachChild(node)
}
}
return tx.Visitor().VisitEachChild(node)
}
func safeMultiLineComment(text string) string {
var b strings.Builder
b.Grow(len(text) + 2)
b.WriteByte(' ')
for {
i := strings.Index(text, "*/")
if i < 0 {
break
}
b.WriteString(text[:i])
b.WriteString("*_/")
text = text[i+2:]
}
b.WriteString(text)
b.WriteByte(' ')
return b.String()
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
package transformers
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/printer"
)
type modifierVisitor struct {
Transformer
AllowedModifiers ast.ModifierFlags
}
func (v *modifierVisitor) visit(node *ast.Node) *ast.Node {
flags := ast.ModifierToFlag(node.Kind)
if flags != ast.ModifierFlagsNone && flags&v.AllowedModifiers == 0 {
return nil
}
return node
}
func ExtractModifiers(emitContext *printer.EmitContext, modifiers *ast.ModifierList, allowed ast.ModifierFlags) *ast.ModifierList {
if modifiers == nil {
return nil
}
tx := modifierVisitor{AllowedModifiers: allowed}
tx.NewTransformer(tx.visit, emitContext)
return tx.visitor.VisitModifiers(modifiers)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,372 @@
package moduletransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type ESModuleTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
resolver binder.ReferenceResolver
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
currentSourceFile *ast.SourceFile
importRequireStatements *importRequireStatements
helperNameSubstitutions map[string]*ast.IdentifierNode
}
type importRequireStatements struct {
statements []*ast.Statement
requireHelperName *ast.IdentifierNode
}
func NewESModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opts.CompilerOptions
tx := &ESModuleTransformer{compilerOptions: compilerOptions, resolver: opts.Resolver, getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile}
return tx.NewTransformer(tx.visit, opts.Context)
}
// Visits source elements that are not top-level or top-level nested statements.
func (tx *ESModuleTransformer) visit(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindSourceFile:
node = tx.visitSourceFile(node.AsSourceFile())
case ast.KindImportDeclaration:
node = tx.visitImportDeclaration(node.AsImportDeclaration())
case ast.KindImportEqualsDeclaration:
node = tx.visitImportEqualsDeclaration(node.AsImportEqualsDeclaration())
case ast.KindExportAssignment:
node = tx.visitExportAssignment(node.AsExportAssignment())
case ast.KindExportDeclaration:
node = tx.visitExportDeclaration(node.AsExportDeclaration())
case ast.KindCallExpression:
node = tx.visitCallExpression(node.AsCallExpression())
default:
node = tx.Visitor().VisitEachChild(node)
}
return node
}
func (tx *ESModuleTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile ||
!(ast.IsExternalModule(node) || tx.compilerOptions.GetIsolatedModules()) {
return node.AsNode()
}
tx.currentSourceFile = node
tx.importRequireStatements = nil
result := tx.Visitor().VisitEachChild(node.AsNode()).AsSourceFile()
tx.EmitContext().AddEmitHelper(result.AsNode(), tx.EmitContext().ReadEmitHelpers()...)
externalHelpersImportDeclaration := createExternalHelpersImportDeclarationIfNeeded(tx.EmitContext(), result, tx.compilerOptions, tx.getEmitModuleFormatOfFile(node), false /*hasExportStarsToExportValues*/, false /*hasImportStar*/, false /*hasImportDefault*/)
if externalHelpersImportDeclaration != nil || tx.importRequireStatements != nil {
prologue, rest := tx.Factory().SplitStandardPrologue(result.Statements.Nodes)
custom, rest := tx.Factory().SplitCustomPrologue(rest)
statements := slices.Clone(prologue)
statements = append(statements, custom...)
if externalHelpersImportDeclaration != nil {
// The helpers import must be visited so that `import x = require("tslib")`
// (TypeScript-only syntax) is transformed to `const x = require("tslib")`
// for CJS output files via visitImportEqualsDeclaration.
statements = append(statements, tx.Visitor().VisitNode(externalHelpersImportDeclaration))
}
if tx.importRequireStatements != nil {
statements = append(statements, tx.importRequireStatements.statements...)
}
statements = append(statements, rest...)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = result.Statements.Loc
result = tx.Factory().UpdateSourceFile(result, statementList, node.EndOfFileToken).AsSourceFile()
}
if ast.IsExternalModule(result) &&
tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindPreserve &&
!core.Some(result.Statements.Nodes, ast.IsExternalModuleIndicator) {
statements := slices.Clone(result.Statements.Nodes)
statements = append(statements, createEmptyImports(tx.Factory()))
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = result.Statements.Loc
result = tx.Factory().UpdateSourceFile(result, statementList, node.EndOfFileToken).AsSourceFile()
}
tx.importRequireStatements = nil
tx.currentSourceFile = nil
return result.AsNode()
}
func (tx *ESModuleTransformer) visitImportDeclaration(node *ast.ImportDeclaration) *ast.Node {
if !tx.compilerOptions.RewriteRelativeImportExtensions.IsTrue() {
return node.AsNode()
}
updatedModuleSpecifier := rewriteModuleSpecifier(tx.EmitContext(), node.ModuleSpecifier, tx.compilerOptions)
return tx.Factory().UpdateImportDeclaration(
node,
nil, /*modifiers*/
tx.Visitor().VisitNode(node.ImportClause),
updatedModuleSpecifier,
tx.Visitor().VisitNode(node.Attributes),
)
}
func (tx *ESModuleTransformer) visitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) *ast.Node {
// Though an error in es2020 modules, in node-flavor es2020 modules, we can helpfully transform this to a synthetic `require` call
// To give easy access to a synchronous `require` in node-flavor esm. We do the transform even in scenarios where we error, but `import.meta.url`
// is available, just because the output is reasonable for a node-like runtime.
if tx.compilerOptions.GetEmitModuleKind() < core.ModuleKindNode16 {
return nil
}
if !ast.IsExternalModuleImportEqualsDeclaration(node.AsNode()) {
panic("import= for internal module references should be handled in an earlier transformer.")
}
varStatement := tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewVariableDeclaration(
node.Name().Clone(tx.Factory()),
nil, /*exclamationToken*/
nil, /*type*/
tx.createRequireCall(node.AsNode()),
),
}),
ast.NodeFlagsConst,
),
)
tx.EmitContext().SetOriginal(varStatement, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode())
var statements []*ast.Statement
statements = append(statements, varStatement)
statements = tx.appendExportsOfImportEqualsDeclaration(statements, node)
return transformers.SingleOrMany(statements, tx.Factory())
}
func (tx *ESModuleTransformer) appendExportsOfImportEqualsDeclaration(statements []*ast.Statement, node *ast.ImportEqualsDeclaration) []*ast.Statement {
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
statements = append(statements, tx.Factory().NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
tx.Factory().NewNamedExports(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewExportSpecifier(
false, /*isTypeOnly*/
nil, /*propertyName*/
node.Name().Clone(tx.Factory()),
),
}),
),
nil, /*moduleSpecifier*/
nil, /*attributes*/
))
}
return statements
}
func (tx *ESModuleTransformer) visitExportAssignment(node *ast.ExportAssignment) *ast.Node {
if !node.IsExportEquals {
return tx.Visitor().VisitEachChild(node.AsNode())
}
if tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindPreserve {
// Elide `export=` as it is not legal with --module ES6
return nil
}
statement := tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(
tx.Factory().NewIdentifier("module"),
nil, /*questionDotToken*/
tx.Factory().NewIdentifier("exports"),
ast.NodeFlagsNone,
),
tx.Visitor().VisitNode(node.Expression),
),
)
tx.EmitContext().SetOriginal(statement, node.AsNode())
return statement
}
func (tx *ESModuleTransformer) visitExportDeclaration(node *ast.ExportDeclaration) *ast.Node {
if node.ModuleSpecifier == nil {
return node.AsNode()
}
updatedModuleSpecifier := rewriteModuleSpecifier(tx.EmitContext(), node.ModuleSpecifier, tx.compilerOptions)
if tx.compilerOptions.Module > core.ModuleKindES2015 || node.ExportClause == nil || !ast.IsNamespaceExport(node.ExportClause) {
// Either ill-formed or don't need to be transformed.
return tx.Factory().UpdateExportDeclaration(
node,
nil, /*modifiers*/
false, /*isTypeOnly*/
node.ExportClause,
updatedModuleSpecifier,
tx.Visitor().VisitNode(node.Attributes),
)
}
oldIdentifier := node.ExportClause.Name()
synthName := tx.Factory().NewGeneratedNameForNode(oldIdentifier)
importDecl := tx.Factory().NewImportDeclaration(
nil, /*modifiers*/
tx.Factory().NewImportClause(
ast.KindUnknown, /*phaseModifier*/
nil, /*name*/
tx.Factory().NewNamespaceImport(synthName),
),
updatedModuleSpecifier,
tx.Visitor().VisitNode(node.Attributes),
)
tx.EmitContext().SetOriginal(importDecl, node.ExportClause)
var exportDecl *ast.Node
if ast.IsExportNamespaceAsDefaultDeclaration(node.AsNode()) {
exportDecl = tx.Factory().NewExportAssignment(nil /*modifiers*/, false /*isExportEquals*/, nil /*typeNode*/, synthName)
} else {
exportDecl = tx.Factory().NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
tx.Factory().NewNamedExports(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewExportSpecifier(false /*isTypeOnly*/, synthName, oldIdentifier),
}),
),
nil, /*moduleSpecifier*/
nil, /*attributes*/
)
}
tx.EmitContext().SetOriginal(exportDecl, node.AsNode())
return transformers.SingleOrMany([]*ast.Statement{importDecl, exportDecl}, tx.Factory())
}
func (tx *ESModuleTransformer) visitCallExpression(node *ast.CallExpression) *ast.Node {
if tx.compilerOptions.RewriteRelativeImportExtensions.IsTrue() {
if ast.IsImportCall(node.AsNode()) && len(node.Arguments.Nodes) > 0 ||
ast.IsInJSFile(node.AsNode()) && ast.IsRequireCall(node.AsNode(), false /*requireStringLiteralLikeArgument*/) {
return tx.visitImportOrRequireCall(node)
}
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *ESModuleTransformer) visitImportOrRequireCall(node *ast.CallExpression) *ast.Node {
if len(node.Arguments.Nodes) == 0 {
return tx.Visitor().VisitEachChild(node.AsNode())
}
expression := tx.Visitor().VisitNode(node.Expression)
var argument *ast.Expression
if ast.IsStringLiteralLike(node.Arguments.Nodes[0]) {
argument = rewriteModuleSpecifier(tx.EmitContext(), node.Arguments.Nodes[0], tx.compilerOptions)
} else {
argument = tx.Factory().NewRewriteRelativeImportExtensionsHelper(node.Arguments.Nodes[0], tx.compilerOptions.Jsx == core.JsxEmitPreserve)
}
var arguments []*ast.Expression
arguments = append(arguments, argument)
rest := core.FirstResult(tx.Visitor().VisitSlice(node.Arguments.Nodes[1:]))
arguments = append(arguments, rest...)
argumentList := tx.Factory().NewNodeList(arguments)
argumentList.Loc = node.Arguments.Loc
return tx.Factory().UpdateCallExpression(
node,
expression,
node.QuestionDotToken,
nil, /*typeArguments*/
argumentList,
node.Flags,
)
}
func (tx *ESModuleTransformer) createRequireCall(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) *ast.Expression {
moduleName := getExternalModuleNameLiteral(tx.Factory(), node, tx.currentSourceFile, nil /*host*/, nil /*emitResolver*/, tx.compilerOptions)
var args []*ast.Expression
if moduleName != nil {
args = append(args, rewriteModuleSpecifier(tx.EmitContext(), moduleName, tx.compilerOptions))
}
if tx.compilerOptions.GetEmitModuleKind() == core.ModuleKindPreserve {
return tx.Factory().NewCallExpression(
tx.Factory().NewIdentifier("require"),
nil, /*questionDotToken*/
nil, /*typeArguments*/
tx.Factory().NewNodeList(args),
ast.NodeFlagsNone,
)
}
if tx.importRequireStatements == nil {
createRequireName := tx.Factory().NewUniqueNameEx("_createRequire", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
importStatement := tx.Factory().NewImportDeclaration(
nil, /*modifiers*/
tx.Factory().NewImportClause(
ast.KindUnknown, /*phaseModifier*/
nil, /*name*/
tx.Factory().NewNamedImports(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewImportSpecifier(
false, /*isTypeOnly*/
tx.Factory().NewIdentifier("createRequire"),
createRequireName,
),
}),
),
),
tx.Factory().NewStringLiteral("module", ast.TokenFlagsNone),
nil, /*attributes*/
)
tx.EmitContext().AddEmitFlags(importStatement, printer.EFCustomPrologue)
requireHelperName := tx.Factory().NewUniqueNameEx("__require", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
requireStatement := tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewVariableDeclaration(
requireHelperName,
nil, /*exclamationToken*/
nil, /*type*/
tx.Factory().NewCallExpression(
createRequireName.Clone(tx.Factory()),
nil, /*questionDotToken*/
nil, /*typeArguments*/
tx.Factory().NewNodeList([]*ast.Expression{
tx.Factory().NewPropertyAccessExpression(
tx.Factory().NewMetaProperty(ast.KindImportKeyword, tx.Factory().NewIdentifier("meta")),
nil, /*questionDotToken*/
tx.Factory().NewIdentifier("url"),
ast.NodeFlagsNone,
),
}),
ast.NodeFlagsNone,
),
),
}),
ast.NodeFlagsConst,
),
)
tx.EmitContext().AddEmitFlags(requireStatement, printer.EFCustomPrologue)
tx.importRequireStatements = &importRequireStatements{
statements: []*ast.Statement{importStatement, requireStatement},
requireHelperName: requireHelperName,
}
}
return tx.Factory().NewCallExpression(
tx.importRequireStatements.requireHelperName.Clone(tx.Factory()),
nil, /*questionDotToken*/
nil, /*typeArguments*/
tx.Factory().NewNodeList(args),
ast.NodeFlagsNone,
)
}

View File

@@ -0,0 +1,390 @@
package moduletransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/stringutil"
"github.com/microsoft/typescript-go/internal/transformers"
)
type externalModuleInfo struct {
externalImports []*ast.Declaration // ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration. imports and reexports of other external modules
exportSpecifiers collections.MultiMap[string, *ast.ExportSpecifier] // Maps local names to their associated export specifiers (excludes reexports)
exportedBindings collections.MultiMap[*ast.Declaration, *ast.ModuleExportName] // Maps local declarations to their associated export aliases
exportedNames []*ast.ModuleExportName // all exported names in the module, both local and re-exported, excluding the names of locally exported function declarations
exportedFunctions collections.OrderedSet[*ast.FunctionDeclarationNode] // all of the top-level exported function declarations
exportEquals *ast.ExportAssignment // an export=/module.exports= declaration if one was present
hasExportStarsToExportValues bool // whether this module contains export*
}
type externalModuleInfoCollector struct {
sourceFile *ast.SourceFile
compilerOptions *core.CompilerOptions
emitContext *printer.EmitContext
resolver binder.ReferenceResolver
uniqueExports collections.Set[string]
hasExportDefault bool
output *externalModuleInfo
}
func collectExternalModuleInfo(sourceFile *ast.SourceFile, compilerOptions *core.CompilerOptions, emitContext *printer.EmitContext, resolver binder.ReferenceResolver) *externalModuleInfo {
c := externalModuleInfoCollector{
sourceFile: sourceFile,
compilerOptions: compilerOptions,
emitContext: emitContext,
resolver: resolver,
output: &externalModuleInfo{},
}
return c.collect()
}
func (c *externalModuleInfoCollector) collect() *externalModuleInfo {
hasImportStar := false
hasImportDefault := false
for _, node := range c.sourceFile.Statements.Nodes {
// Look through NotEmittedStatement to find elided export= declarations
// (e.g., `declare export = x` is elided by the type eraser but must still be collected)
if ast.IsNotEmittedStatement(node) {
original := c.emitContext.MostOriginal(node)
if original != nil && ast.IsExportAssignment(original) {
n := original.AsExportAssignment()
if n.IsExportEquals && c.output.exportEquals == nil {
c.output.exportEquals = n
}
}
continue
}
switch node.Kind {
case ast.KindImportDeclaration:
// import "mod"
// import x from "mod"
// import * as x from "mod"
// import { x, y } from "mod"
n := node.AsImportDeclaration()
c.addExternalImport(node)
if !hasImportStar && getImportNeedsImportStarHelper(n) {
hasImportStar = true
}
if !hasImportDefault && getImportNeedsImportDefaultHelper(n) {
hasImportDefault = true
}
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
if ast.IsExternalModuleReference(n.ModuleReference) {
// import x = require("mod")
c.addExternalImport(node)
}
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
if n.ModuleSpecifier != nil {
// export * from "mod"
// export * as ns from "mod"
// export { x, y } from "mod"
c.addExternalImport(node)
if n.ExportClause == nil {
// export * from "mod"
c.output.hasExportStarsToExportValues = true
} else if ast.IsNamedExports(n.ExportClause) {
// export { x, y } from "mod"
c.addExportedNamesForExportDeclaration(n)
if !hasImportDefault {
hasImportDefault = containsDefaultReference(n.ExportClause)
}
} else {
// export * as ns from "mod"
name := n.ExportClause.AsNamespaceExport().Name()
nameText := name.Text()
if c.addUniqueExport(nameText) {
c.addExportedBinding(node, name)
c.addExportedName(name)
}
// we use the same helpers for `export * as ns` as we do for `import * as ns`
hasImportStar = true
}
} else {
// export { x, y }
c.addExportedNamesForExportDeclaration(node.AsExportDeclaration())
}
case ast.KindExportAssignment:
n := node.AsExportAssignment()
if n.IsExportEquals && c.output.exportEquals == nil {
// export = x
c.output.exportEquals = n
}
case ast.KindVariableStatement:
n := node.AsVariableStatement()
if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) {
for _, decl := range n.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
c.collectExportedVariableInfo(decl)
}
}
case ast.KindFunctionDeclaration:
n := node.AsFunctionDeclaration()
if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) {
c.addExportedFunctionDeclaration(n, nil /*name*/, ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault))
}
case ast.KindClassDeclaration:
n := node.AsClassDeclaration()
if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) {
if ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) {
// export default class { }
if !c.hasExportDefault {
name := n.Name()
if name == nil {
name = c.emitContext.Factory.NewGeneratedNameForNode(node)
}
c.addExportedBinding(node, name)
c.hasExportDefault = true
}
} else {
// export class x { }
name := n.Name()
if name != nil {
if c.addUniqueExport(name.Text()) {
c.addExportedBinding(node, name)
c.addExportedName(name)
}
}
}
}
}
}
return c.output
}
func (c *externalModuleInfoCollector) addUniqueExport(name string) bool {
if !c.uniqueExports.Has(name) {
c.uniqueExports.Add(name)
return true
}
return false
}
func (c *externalModuleInfoCollector) addExportedBinding(decl *ast.Declaration, name *ast.ModuleExportName) {
c.output.exportedBindings.Add(c.emitContext.MostOriginal(decl), name)
}
func (c *externalModuleInfoCollector) addExternalImport(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) {
c.output.externalImports = append(c.output.externalImports, node)
}
func (c *externalModuleInfoCollector) addExportedName(name *ast.ModuleExportName) {
c.output.exportedNames = append(c.output.exportedNames, name)
}
func (c *externalModuleInfoCollector) addExportedNamesForExportDeclaration(node *ast.ExportDeclaration) {
for _, specifier := range node.ExportClause.Elements() {
specifierNameText := specifier.Name().Text()
if c.addUniqueExport(specifierNameText) {
name := specifier.PropertyNameOrName()
if name.Kind != ast.KindStringLiteral {
if node.ModuleSpecifier == nil {
c.output.exportSpecifiers.Add(name.Text(), specifier.AsExportSpecifier())
}
decl := c.resolver.GetReferencedImportDeclaration(c.emitContext.MostOriginal(name))
if decl == nil {
decl = c.resolver.GetReferencedValueDeclaration(c.emitContext.MostOriginal(name))
}
if decl != nil {
if decl.Kind == ast.KindFunctionDeclaration {
c.uniqueExports.Delete(specifierNameText)
c.addExportedFunctionDeclaration(decl.AsFunctionDeclaration(), specifier.Name(), ast.ModuleExportNameIsDefault(specifier.Name()))
continue
}
c.addExportedBinding(decl, specifier.Name())
}
}
c.addExportedName(specifier.Name())
}
}
}
func (c *externalModuleInfoCollector) addExportedFunctionDeclaration(node *ast.FunctionDeclaration, name *ast.ModuleExportName, isDefault bool) {
c.output.exportedFunctions.Add(c.emitContext.MostOriginal(node.AsNode()))
if isDefault {
// export default function() { }
// function x() { } + export { x as default };
if !c.hasExportDefault {
if name == nil {
name = c.emitContext.Factory.NewGeneratedNameForNode(node.AsNode())
}
c.addExportedBinding(node.AsNode(), name)
c.hasExportDefault = true
}
} else {
// export function x() { }
// function x() { } + export { x }
if name == nil {
name = node.Name()
}
nameText := name.Text()
if c.addUniqueExport(nameText) {
c.addExportedBinding(node.AsNode(), name)
}
}
}
func (c *externalModuleInfoCollector) collectExportedVariableInfo(decl *ast.Node /*VariableDeclaration | BindingElement*/) {
if ast.IsBindingPattern(decl.Name()) {
for _, element := range decl.Name().Elements() {
e := element.AsBindingElement()
if e.Name() != nil {
c.collectExportedVariableInfo(element)
}
}
} else if !c.emitContext.HasAutoGenerateInfo(decl.Name()) {
text := decl.Name().Text()
if c.addUniqueExport(text) {
c.addExportedName(decl.Name())
if transformers.IsLocalName(c.emitContext, decl.Name()) {
c.addExportedBinding(decl, decl.Name())
}
}
}
}
const externalHelpersModuleNameText = "tslib"
func createExternalHelpersImportDeclarationIfNeeded(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, compilerOptions *core.CompilerOptions, fileModuleKind core.ModuleKind, hasExportStarsToExportValues bool, hasImportStar bool, hasImportDefault bool) *ast.Node /*ImportDeclaration | ImportEqualsDeclaration*/ {
if compilerOptions.ImportHelpers.IsTrue() && ast.IsEffectiveExternalModule(sourceFile, compilerOptions) {
moduleKind := compilerOptions.GetEmitModuleKind()
helpers := getImportedHelpers(emitContext, sourceFile)
if fileModuleKind == core.ModuleKindCommonJS || fileModuleKind == core.ModuleKindNone && moduleKind == core.ModuleKindCommonJS {
// When we emit to a non-ES module, generate a synthetic `import tslib = require("tslib")` to be further transformed.
externalHelpersModuleName := getOrCreateExternalHelpersModuleNameIfNeeded(emitContext, sourceFile, compilerOptions, helpers, hasExportStarsToExportValues, hasImportStar || hasImportDefault, fileModuleKind)
if externalHelpersModuleName != nil {
externalHelpersImportDeclaration := emitContext.Factory.NewImportEqualsDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
externalHelpersModuleName,
emitContext.Factory.NewExternalModuleReference(emitContext.Factory.NewStringLiteral(externalHelpersModuleNameText, ast.TokenFlagsNone)),
)
emitContext.AddEmitFlags(externalHelpersImportDeclaration, printer.EFCustomPrologue)
return externalHelpersImportDeclaration
}
} else {
// When we emit as an ES module, generate an `import` declaration that uses named imports for helpers.
// If we cannot determine the implied module kind under `module: preserve` we assume ESM.
var helperNames []string
for _, helper := range helpers {
importName := helper.ImportName
if len(importName) > 0 {
helperNames = core.AppendIfUnique(helperNames, importName)
}
}
if len(helperNames) > 0 {
slices.SortFunc(helperNames, stringutil.CompareStringsCaseSensitive)
// Alias the imports if the names are used somewhere in the file.
// NOTE: We don't need to care about global import collisions as this is a module.
importSpecifiers := core.Map(helperNames, func(name string) *ast.ImportSpecifierNode {
if printer.IsFileLevelUniqueName(sourceFile, name, nil /*hasGlobalName*/) {
return emitContext.Factory.NewImportSpecifier(false /*isTypeOnly*/, nil /*propertyName*/, emitContext.Factory.NewIdentifier(name))
} else {
return emitContext.Factory.NewImportSpecifier(false /*isTypeOnly*/, emitContext.Factory.NewIdentifier(name), emitContext.Factory.NewUnscopedHelperName(name))
}
})
namedBindings := emitContext.Factory.NewNamedImports(emitContext.Factory.NewNodeList(importSpecifiers))
parseNode := emitContext.MostOriginal(sourceFile.AsNode())
emitContext.AddEmitFlags(parseNode, printer.EFExternalHelpers)
externalHelpersImportDeclaration := emitContext.Factory.NewImportDeclaration(
nil, /*modifiers*/
emitContext.Factory.NewImportClause(ast.KindUnknown /*phaseModifier*/, nil /*name*/, namedBindings),
emitContext.Factory.NewStringLiteral(externalHelpersModuleNameText, ast.TokenFlagsNone),
nil, /*attributes*/
)
emitContext.AddEmitFlags(externalHelpersImportDeclaration, printer.EFCustomPrologue)
return externalHelpersImportDeclaration
}
}
}
return nil
}
func getImportedHelpers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile) []*printer.EmitHelper {
var helpers []*printer.EmitHelper
for _, helper := range emitContext.GetEmitHelpers(sourceFile.AsNode()) {
if !helper.Scoped {
helpers = append(helpers, helper)
}
}
return helpers
}
func getOrCreateExternalHelpersModuleNameIfNeeded(emitContext *printer.EmitContext, node *ast.SourceFile, compilerOptions *core.CompilerOptions, helpers []*printer.EmitHelper, hasExportStarsToExportValues bool, hasImportStarOrImportDefault bool, fileModuleKind core.ModuleKind) *ast.IdentifierNode {
externalHelpersModuleName := emitContext.GetExternalHelpersModuleName(node)
if externalHelpersModuleName != nil {
return externalHelpersModuleName
}
create := len(helpers) > 0 ||
(hasExportStarsToExportValues || hasImportStarOrImportDefault) &&
fileModuleKind < core.ModuleKindSystem
if create {
externalHelpersModuleName = emitContext.Factory.NewUniqueName(externalHelpersModuleNameText)
emitContext.SetExternalHelpersModuleName(node, externalHelpersModuleName)
}
return externalHelpersModuleName
}
func isNamedDefaultReference(e *ast.Node /*ImportSpecifier | ExportSpecifier*/) bool {
return ast.ModuleExportNameIsDefault(e.PropertyNameOrName())
}
func containsDefaultReference(node *ast.Node /*NamedImportBindings | NamedExportBindings*/) bool {
return node != nil && (ast.IsNamedImports(node) || ast.IsNamedExports(node)) && core.Some(node.Elements(), isNamedDefaultReference)
}
func getExportNeedsImportStarHelper(node *ast.ExportDeclaration) bool {
return ast.GetNamespaceDeclarationNode(node.AsNode()) != nil
}
func getImportNeedsImportStarHelper(node *ast.ImportDeclaration) bool {
if ast.GetNamespaceDeclarationNode(node.AsNode()) != nil {
return true
}
if node.ImportClause == nil {
return false
}
bindings := node.ImportClause.AsImportClause().NamedBindings
if bindings == nil {
return false
}
if !ast.IsNamedImports(bindings) {
return false
}
namedImports := bindings.AsNamedImports()
defaultRefCount := 0
for _, binding := range namedImports.Elements.Nodes {
if isNamedDefaultReference(binding) {
defaultRefCount++
}
}
// Import star is required if there's default named refs mixed with non-default refs, or if theres non-default refs and it has a default import
return (defaultRefCount > 0 && defaultRefCount != len(namedImports.Elements.Nodes)) || ((len(namedImports.Elements.Nodes)-defaultRefCount) != 0 && ast.IsDefaultImport(node.AsNode()))
}
func getImportNeedsImportDefaultHelper(node *ast.ImportDeclaration) bool {
// Import default is needed if there's a default import or a default ref and no other refs (meaning an import star helper wasn't requested)
return !getImportNeedsImportStarHelper(node) && (ast.IsDefaultImport(node.AsNode()) || (node.ImportClause != nil &&
ast.IsNamedImports(node.ImportClause.AsImportClause().NamedBindings) &&
containsDefaultReference(node.ImportClause.AsImportClause().NamedBindings)))
}

View File

@@ -0,0 +1,53 @@
package moduletransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
type ImpliedModuleTransformer struct {
transformers.Transformer
opts *transformers.TransformOptions
resolver binder.ReferenceResolver
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
cjsTransformer *transformers.Transformer
esmTransformer *transformers.Transformer
}
func NewImpliedModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &ImpliedModuleTransformer{opts: opts, resolver: opts.Resolver, getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile}
return tx.NewTransformer(tx.visit, opts.Context)
}
func (tx *ImpliedModuleTransformer) visit(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindSourceFile:
node = tx.visitSourceFile(node.AsSourceFile())
}
return node
}
func (tx *ImpliedModuleTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
format := tx.getEmitModuleFormatOfFile(node)
var transformer *transformers.Transformer
if format >= core.ModuleKindES2015 {
if tx.esmTransformer == nil {
tx.esmTransformer = NewESModuleTransformer(tx.opts)
}
transformer = tx.esmTransformer
} else {
if tx.cjsTransformer == nil {
tx.cjsTransformer = NewCommonJSModuleTransformer(tx.opts)
}
transformer = tx.cjsTransformer
}
return transformer.TransformSourceFile(node).AsNode()
}

View File

@@ -0,0 +1,118 @@
package moduletransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/outputpaths"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
"github.com/microsoft/typescript-go/internal/tspath"
)
func isDeclarationNameOfEnumOrNamespace(emitContext *printer.EmitContext, node *ast.IdentifierNode) bool {
if original := emitContext.MostOriginal(node); original != nil && original.Parent != nil { //nolint:customlint // MostOriginal yields parse-tree nodes and this helper intentionally inspects parse-tree parents.
switch original.Parent.Kind { //nolint:customlint // MostOriginal yields parse-tree nodes and this helper intentionally inspects parse-tree parents.
case ast.KindEnumDeclaration, ast.KindModuleDeclaration:
return original == original.Parent.Name() //nolint:customlint // MostOriginal yields parse-tree nodes and this helper intentionally inspects parse-tree parents.
}
}
return false
}
func rewriteModuleSpecifier(emitContext *printer.EmitContext, node *ast.Expression, compilerOptions *core.CompilerOptions) *ast.Expression {
if node == nil || !ast.IsStringLiteral(node) || !core.ShouldRewriteModuleSpecifier(node.Text(), compilerOptions) {
return node
}
updatedText := tspath.ChangeExtension(node.Text(), outputpaths.GetOutputExtension(node.Text(), compilerOptions.Jsx))
if updatedText != node.Text() {
updated := emitContext.Factory.NewStringLiteral(updatedText, node.AsStringLiteral().TokenFlags)
emitContext.SetOriginal(updated, node)
emitContext.AssignCommentAndSourceMapRanges(updated, node)
return updated
}
return node
}
func createEmptyImports(factory *printer.NodeFactory) *ast.Statement {
return factory.NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
factory.NewNamedExports(factory.NewNodeList(nil)),
nil, /*moduleSpecifier*/
nil, /*attributes*/
)
}
// Get the name of a target module from an import/export declaration as should be written in the emitted output.
// The emitted output name can be different from the input if:
// 1. The module has a /// <amd-module name="<new name>" />
// 2. --out or --outFile is used, making the name relative to the rootDir
// 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System).
//
// Otherwise, a new StringLiteral node representing the module name will be returned.
func getExternalModuleNameLiteral(factory *printer.NodeFactory, importNode *ast.Node /*ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall*/, sourceFile *ast.SourceFile, host any /*EmitHost*/, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions) *ast.StringLiteralNode {
moduleName := ast.GetExternalModuleName(importNode)
if moduleName != nil && ast.IsStringLiteral(moduleName) {
name := tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions)
if name == nil {
name = tryRenameExternalModule(factory, moduleName, sourceFile)
}
if name == nil { // !!! propagate token flags (will produce new diffs)
name = factory.NewStringLiteral(moduleName.Text(), ast.TokenFlagsNone)
}
return name
}
return nil
}
// Get the name of a module as should be written in the emitted output.
// The emitted output name can be different from the input if:
// 1. The module has a /// <amd-module name="<new name>" />
// 2. --out or --outFile is used, making the name relative to the rootDir
//
// Otherwise, a new StringLiteral node representing the module name will be returned.
func tryGetModuleNameFromFile(factory *printer.NodeFactory, file *ast.SourceFile, host any /*EmitHost*/, options *core.CompilerOptions) *ast.StringLiteralNode {
if file == nil {
return nil
}
// !!!
// if file.moduleName {
// return factory.createStringLiteral(file.moduleName)
// }
return nil
}
func tryGetModuleNameFromDeclaration(declaration *ast.Node /*ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall*/, host any /*EmitHost*/, factory *printer.NodeFactory, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions) *ast.StringLiteralNode {
if resolver == nil {
return nil
}
return tryGetModuleNameFromFile(factory, resolver.GetExternalModuleFileFromDeclaration(declaration), host, compilerOptions)
}
// Resolves a local path to a path which is absolute to the base of the emit
func getExternalModuleNameFromPath(host any /*ResolveModuleNameResolutionHost*/, fileName string, referencePath string) string {
// !!!
return ""
}
// Some bundlers (SystemJS builder) sometimes want to rename dependencies.
// Here we check if alternative name was provided for a given moduleName and return it if possible.
func tryRenameExternalModule(factory *printer.NodeFactory, moduleName *ast.LiteralExpression, sourceFile *ast.SourceFile) *ast.StringLiteralNode {
// !!!
return nil
}
func isFileLevelReservedGeneratedIdentifier(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
info := emitContext.GetAutoGenerateInfo(name)
return info != nil &&
info.Flags.IsFileLevel() &&
info.Flags.IsOptimistic() &&
info.Flags.IsReservedInNestedScopes()
}
// A simple inlinable expression is an expression which can be copied into multiple locations
// without risk of repeating any sideeffects and whose value could not possibly change between
// any such locations
func isSimpleInlineableExpression(expression *ast.Expression) bool {
return !ast.IsIdentifier(expression) && transformers.IsSimpleCopiableExpression(expression)
}

View File

@@ -0,0 +1,41 @@
package transformers
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/printer"
)
type Transformer struct {
emitContext *printer.EmitContext
factory *printer.NodeFactory
visitor *ast.NodeVisitor
}
func (tx *Transformer) NewTransformer(visit func(node *ast.Node) *ast.Node, emitContext *printer.EmitContext) *Transformer {
if tx.emitContext != nil {
panic("Transformer already initialized")
}
if emitContext == nil {
emitContext = printer.NewEmitContext()
}
tx.emitContext = emitContext
tx.factory = emitContext.Factory
tx.visitor = emitContext.NewNodeVisitor(visit)
return tx
}
func (tx *Transformer) EmitContext() *printer.EmitContext {
return tx.emitContext
}
func (tx *Transformer) Visitor() *ast.NodeVisitor {
return tx.visitor
}
func (tx *Transformer) Factory() *printer.NodeFactory {
return tx.factory
}
func (tx *Transformer) TransformSourceFile(file *ast.SourceFile) *ast.SourceFile {
return tx.visitor.VisitSourceFile(file)
}

View File

@@ -0,0 +1,153 @@
package tstransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type ImportElisionTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
currentSourceFile *ast.SourceFile
emitResolver printer.EmitResolver
}
func NewImportElisionTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opt.CompilerOptions
emitContext := opt.Context
if compilerOptions.VerbatimModuleSyntax.IsTrue() {
panic("ImportElisionTransformer should not be used with VerbatimModuleSyntax")
}
tx := &ImportElisionTransformer{compilerOptions: compilerOptions, emitResolver: opt.EmitResolver}
return tx.NewTransformer(tx.visit, emitContext)
}
func (tx *ImportElisionTransformer) visit(node *ast.Node) *ast.Node {
if ast.IsSourceFile(node) && tx.emitResolver != nil {
tx.emitResolver.MarkLinkedReferencesRecursively(tx.EmitContext().MostOriginal(node).AsSourceFile())
}
switch node.Kind {
case ast.KindImportEqualsDeclaration:
if ast.IsExternalModuleImportEqualsDeclaration(node) {
if !tx.shouldEmitAliasDeclaration(node) {
return nil
}
} else {
if !tx.shouldEmitImportEqualsDeclaration(node.AsImportEqualsDeclaration()) {
return nil
}
}
return tx.Visitor().VisitEachChild(node)
case ast.KindImportDeclaration:
n := node.AsImportDeclaration()
// Do not elide a side-effect only import declaration.
// import "foo";
if n.ImportClause != nil {
importClause := tx.Visitor().VisitNode(n.ImportClause)
if importClause == nil {
return nil
}
return tx.Factory().UpdateImportDeclaration(n, n.Modifiers(), importClause, n.ModuleSpecifier, tx.Visitor().VisitNode(n.Attributes))
}
return tx.Visitor().VisitEachChild(node)
case ast.KindImportClause:
n := node.AsImportClause()
name := core.IfElse(tx.shouldEmitAliasDeclaration(node), n.Name(), nil)
namedBindings := tx.Visitor().VisitNode(n.NamedBindings)
if name == nil && namedBindings == nil {
// all import bindings were elided
return nil
}
return tx.Factory().UpdateImportClause(n, n.PhaseModifier, name, namedBindings)
case ast.KindNamespaceImport:
if !tx.shouldEmitAliasDeclaration(node) {
// elide unused imports
return nil
}
return node
case ast.KindNamedImports:
n := node.AsNamedImports()
elements := tx.Visitor().VisitNodes(n.Elements)
if len(elements.Nodes) == 0 {
// all import specifiers were elided
return nil
}
return tx.Factory().UpdateNamedImports(n, elements)
case ast.KindImportSpecifier:
if !tx.shouldEmitAliasDeclaration(node) {
// elide type-only or unused imports
return nil
}
return node
case ast.KindExportAssignment:
if !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && !tx.isValueAliasDeclaration(node) {
// elide unused import
return nil
}
return tx.Visitor().VisitEachChild(node)
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
var exportClause *ast.Node
if n.ExportClause != nil {
exportClause = tx.Visitor().VisitNode(n.ExportClause)
if exportClause == nil {
// all export bindings were elided
return nil
}
}
return tx.Factory().UpdateExportDeclaration(n, nil /*modifiers*/, false /*isTypeOnly*/, exportClause, tx.Visitor().VisitNode(n.ModuleSpecifier), tx.Visitor().VisitNode(n.Attributes))
case ast.KindNamedExports:
n := node.AsNamedExports()
elements := tx.Visitor().VisitNodes(n.Elements)
if len(elements.Nodes) == 0 {
// all export specifiers were elided
return nil
}
return tx.Factory().UpdateNamedExports(n, elements)
case ast.KindExportSpecifier:
if !tx.isValueAliasDeclaration(node) {
// elide unused export
return nil
}
return node
case ast.KindSourceFile:
savedCurrentSourceFile := tx.currentSourceFile
tx.currentSourceFile = node.AsSourceFile()
node = tx.Visitor().VisitEachChild(node)
tx.currentSourceFile = savedCurrentSourceFile
return node
case ast.KindModuleDeclaration, ast.KindModuleBlock:
return tx.Visitor().VisitEachChild(node)
default:
return node
}
}
func (tx *ImportElisionTransformer) shouldEmitAliasDeclaration(node *ast.Node) bool {
return ast.IsInJSFile(node) || tx.isReferencedAliasDeclaration(node)
}
func (tx *ImportElisionTransformer) shouldEmitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) bool {
// preserve old compiler's behavior: emit import declaration (even if we do not consider them referenced) when
// - current file is not external module
// - import declaration is top level and target is value imported by entity name
return tx.shouldEmitAliasDeclaration(node.AsNode()) || (!ast.IsExternalModule(tx.currentSourceFile) && tx.isTopLevelValueImportEqualsWithEntityName(node.AsNode()))
}
func (tx *ImportElisionTransformer) isReferencedAliasDeclaration(node *ast.Node) bool {
node = tx.EmitContext().ParseNode(node)
return node == nil || tx.emitResolver.IsReferencedAliasDeclaration(node)
}
func (tx *ImportElisionTransformer) isValueAliasDeclaration(node *ast.Node) bool {
node = tx.EmitContext().ParseNode(node)
return node == nil || tx.emitResolver.IsValueAliasDeclaration(node)
}
func (tx *ImportElisionTransformer) isTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool {
node = tx.EmitContext().ParseNode(node)
return node != nil && tx.emitResolver.IsTopLevelValueImportEqualsWithEntityName(node)
}

View File

@@ -0,0 +1,270 @@
package tstransforms_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/checker"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/module"
"github.com/microsoft/typescript-go/internal/packagejson"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/symlinks"
"github.com/microsoft/typescript-go/internal/testutil/emittestutil"
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
"github.com/microsoft/typescript-go/internal/transformers"
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
"github.com/microsoft/typescript-go/internal/tsoptions"
"github.com/microsoft/typescript-go/internal/tspath"
)
type fakeProgram struct {
singleThreaded bool
compilerOptions *core.CompilerOptions
files []*ast.SourceFile
getEmitModuleFormatOfFile func(sourceFile ast.HasFileName) core.ModuleKind
getImpliedNodeFormatForEmit func(sourceFile ast.HasFileName) core.ModuleKind
getResolvedModule func(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
getSourceFile func(FileName string) *ast.SourceFile
getSourceFileForResolvedModule func(FileName string) *ast.SourceFile
}
// GetRedirectForResolution implements checker.Program.
func (p *fakeProgram) GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine {
panic("unimplemented")
}
// SourceFileMayBeEmitted implements checker.Program.
func (p *fakeProgram) SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool {
panic("unimplemented")
}
// GetEmitSyntaxForUsageLocation implements checker.Program.
func (p *fakeProgram) GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, usageLocation *ast.StringLiteralLike) core.ResolutionMode {
panic("unimplemented")
}
// CommonSourceDirectory implements checker.Program.
func (p *fakeProgram) CommonSourceDirectory() string {
panic("unimplemented")
}
func (p *fakeProgram) GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule {
panic("unimplemented")
}
func (p *fakeProgram) FileExists(path string) bool {
return false
}
func (p *fakeProgram) GetCurrentDirectory() string {
return ""
}
func (p *fakeProgram) GetGlobalTypingsCacheLocation() string {
return ""
}
func (p *fakeProgram) GetNearestAncestorDirectoryWithPackageJson(dirname string) string {
return ""
}
func (p *fakeProgram) GetSymlinkCache() *symlinks.KnownSymlinks {
return nil
}
func (p *fakeProgram) ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode) *module.ResolvedModule {
return nil
}
func (p *fakeProgram) GetPackageJsonInfo(pkgJsonPath string) *packagejson.InfoCacheEntry {
return nil
}
func (p *fakeProgram) GetRedirectTargets(path tspath.Path) []string {
return nil
}
func (p *fakeProgram) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string {
return ""
}
func (p *fakeProgram) GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference {
return nil
}
func (p *fakeProgram) IsSourceFromProjectReference(path tspath.Path) bool {
return false
}
func (p *fakeProgram) GetPackagesMap() map[string]bool {
return nil
}
func (p *fakeProgram) GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference {
return nil
}
func (p *fakeProgram) UseCaseSensitiveFileNames() bool {
return true
}
func (p *fakeProgram) Options() *core.CompilerOptions {
return p.compilerOptions
}
func (p *fakeProgram) SourceFiles() []*ast.SourceFile {
return p.files
}
func (p *fakeProgram) BindSourceFiles() {
wg := core.NewWorkGroup(p.singleThreaded)
for _, file := range p.files {
if !file.IsBound() {
wg.Queue(func() {
binder.BindSourceFile(file)
})
}
}
wg.RunAndWait()
}
func (p *fakeProgram) GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind {
return p.getEmitModuleFormatOfFile(sourceFile)
}
func (p *fakeProgram) GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind {
return p.getImpliedNodeFormatForEmit(sourceFile)
}
func (p *fakeProgram) GetDefaultResolutionModeForFile(sourceFile ast.HasFileName) core.ResolutionMode {
return p.getEmitModuleFormatOfFile(sourceFile)
}
func (p *fakeProgram) GetModeForUsageLocation(sourceFile ast.HasFileName, location *ast.Node) core.ResolutionMode {
return p.getEmitModuleFormatOfFile(sourceFile)
}
func (p *fakeProgram) GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule {
return p.getResolvedModule(currentSourceFile, moduleReference, mode)
}
func (p *fakeProgram) GetSourceFile(FileName string) *ast.SourceFile {
return p.getSourceFile(FileName)
}
func (p *fakeProgram) GetSourceFileForResolvedModule(FileName string) *ast.SourceFile {
return p.getSourceFileForResolvedModule(FileName)
}
func (p *fakeProgram) GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData {
return ast.SourceFileMetaData{}
}
func (p *fakeProgram) GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node {
return nil
}
func (p *fakeProgram) GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node) {
return "", nil
}
func (p *fakeProgram) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule] {
panic("unimplemented")
}
func (p *fakeProgram) IsSourceFileDefaultLibrary(path tspath.Path) bool {
return false
}
func TestImportElision(t *testing.T) {
t.Parallel()
data := []struct {
title string
input string
output string
other string
jsx bool
}{
{title: "ImportEquals#1", input: "import x = require(\"other\"); x;", output: "import x = require(\"other\");\nx;"},
{title: "ImportEquals#2", input: "import x = require(\"other\");", output: ""},
{title: "ImportDeclaration#1", input: `import "m";`, output: `import "m";`},
{title: "ImportDeclaration#2", input: "import * as x from \"other\"; x;", output: "import * as x from \"other\";\nx;"},
{title: "ImportDeclaration#3", input: "import x from \"other\"; x;", output: "import x from \"other\";\nx;"},
{title: "ImportDeclaration#4", input: "import { x } from \"other\"; x;", output: "import { x } from \"other\";\nx;"},
{title: "ImportDeclaration#5", input: "import * as x from \"other\";", output: ""},
{title: "ImportDeclaration#6", input: "import x from \"other\";", output: ""},
{title: "ImportDeclaration#7", input: "import { x } from \"other\";", output: ""},
{title: "ExportDeclaration#1", input: "export * from \"other\";", other: "export let x;", output: "export * from \"other\";"},
{title: "ExportDeclaration#2", input: "export * as x from \"other\";", other: "export let x;", output: "export * as x from \"other\";"},
{title: "ExportDeclaration#3", input: "export * from \"other\";", other: "export let x;", output: "export * from \"other\";"},
{title: "ExportDeclaration#4", input: "export * as x from \"other\";", other: "export let x;", output: "export * as x from \"other\";"},
{title: "ExportDeclaration#5", input: "export { x } from \"other\";", other: "export let x;", output: "export { x } from \"other\";"},
{title: "ExportDeclaration#6", input: "export { x } from \"other\";", other: "export type x = any;", output: ""},
{title: "ExportDeclaration#7", input: "export { x }; let x;", output: "export { x };\nlet x;"},
{title: "ExportDeclaration#8", input: "export { x }; type x = any;", output: ""},
{title: "ExportDeclaration#9", input: "import { x } from \"other\"; export { x };", other: "export type x = any;", output: ""},
{title: "ExportAssignment#1", input: "let x; export default x;", output: "let x;\nexport default x;"},
{title: "ExportAssignment#2", input: "type x = any; export default x;", output: ""},
}
for _, rec := range data {
t.Run(rec.title, func(t *testing.T) {
t.Parallel()
file := parsetestutil.ParseTypeScript(rec.input, rec.jsx)
parsetestutil.CheckDiagnostics(t, file)
files := []*ast.SourceFile{file}
var other *ast.SourceFile
if len(rec.other) > 0 {
other = parsetestutil.ParseTypeScript(rec.other, rec.jsx)
parsetestutil.CheckDiagnostics(t, other)
files = append(files, other)
}
compilerOptions := &core.CompilerOptions{}
c, _ := checker.NewChecker(&fakeProgram{
singleThreaded: true,
compilerOptions: compilerOptions,
files: files,
getEmitModuleFormatOfFile: func(sourceFile ast.HasFileName) core.ModuleKind {
return core.ModuleKindESNext
},
getImpliedNodeFormatForEmit: func(sourceFile ast.HasFileName) core.ModuleKind {
return core.ModuleKindESNext
},
getSourceFile: func(fileName string) *ast.SourceFile {
if fileName == "other.ts" {
return other
}
return nil
},
getSourceFileForResolvedModule: func(fileName string) *ast.SourceFile {
if fileName == "other.ts" {
return other
}
return nil
},
getResolvedModule: func(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule {
if currentSourceFile == file && moduleReference == "other" {
return &module.ResolvedModule{
ResolvedFileName: "other.ts",
Extension: tspath.ExtensionTs,
}
}
return nil
},
}, nil)
emitResolver := c.GetEmitResolver()
opts := &transformers.TransformOptions{CompilerOptions: compilerOptions, Context: printer.NewEmitContext(), EmitResolver: emitResolver, Resolver: emitResolver}
file = tstransforms.NewTypeEraserTransformer(opts).TransformSourceFile(file)
file = tstransforms.NewImportElisionTransformer(opts).TransformSourceFile(file)
emittestutil.CheckEmit(t, nil, file, rec.output)
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,390 @@
package tstransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
const USE_NEW_TYPE_METADATA_FORMAT = false
type MetadataTransformer struct {
transformers.Transformer
legacyDecorators bool
resolver printer.EmitResolver
serializer *metadataSerializer
languageVersion core.ScriptTarget
strictNullChecks bool
parent *ast.Node
currentLexicalScope *ast.Node
}
func NewMetadataTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
tx := &MetadataTransformer{
legacyDecorators: opt.CompilerOptions.ExperimentalDecorators.IsTrue(),
resolver: opt.EmitResolver,
languageVersion: opt.CompilerOptions.GetEmitScriptTarget(),
strictNullChecks: opt.CompilerOptions.GetStrictOptionValue(opt.CompilerOptions.StrictNullChecks),
}
return tx.NewTransformer(tx.visit, opt.Context)
}
func (tx *MetadataTransformer) visit(node *ast.Node) *ast.Node {
if (node.SubtreeFacts() & ast.SubtreeContainsDecorators) == 0 {
return node
}
switch node.Kind {
case ast.KindClassDeclaration:
return tx.visitClassDeclaration(node.AsClassDeclaration())
case ast.KindClassExpression:
return tx.visitClassExpression(node.AsClassExpression())
case ast.KindPropertyDeclaration:
return tx.visitPropertyDeclaration(node.AsPropertyDeclaration())
case ast.KindMethodDeclaration:
return tx.visitMethodDeclaration(node.AsMethodDeclaration())
case ast.KindSetAccessor:
return tx.visitSetAccessor(node.AsSetAccessorDeclaration())
case ast.KindGetAccessor:
return tx.visitGetAccessor(node.AsGetAccessorDeclaration())
case ast.KindSourceFile:
tx.parent = nil
defer tx.setParent(nil)
tx.currentLexicalScope = node
defer tx.setCurrentLexicalScope(nil)
tx.serializer = newMetadataSerializer(tx.resolver, tx.Factory(), tx.EmitContext(), tx.languageVersion, tx.strictNullChecks)
updated := tx.Visitor().VisitEachChild(node)
tx.EmitContext().AddEmitHelper(updated, tx.EmitContext().ReadEmitHelpers()...)
return updated
case ast.KindModuleBlock, ast.KindBlock, ast.KindCaseBlock:
oldScope := tx.currentLexicalScope
tx.currentLexicalScope = node
defer tx.setCurrentLexicalScope(oldScope)
return tx.Visitor().VisitEachChild(node)
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *MetadataTransformer) setParent(node *ast.Node) {
tx.parent = node
}
func (tx *MetadataTransformer) setCurrentLexicalScope(node *ast.Node) {
tx.currentLexicalScope = node
}
func (tx *MetadataTransformer) visitClassExpression(node *ast.ClassExpression) *ast.Node {
oldParent := tx.parent
tx.parent = node.AsNode()
defer tx.setParent(oldParent)
if !ast.ClassOrConstructorParameterIsDecorated(tx.legacyDecorators, node.AsNode()) {
return tx.Visitor().VisitEachChild(node.AsNode())
}
modifiers := tx.injectClassTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode())
return tx.Factory().UpdateClassExpression(
node,
modifiers,
tx.Visitor().VisitNode(node.Name()),
tx.Visitor().VisitNodes(node.TypeParameters),
tx.Visitor().VisitNodes(node.HeritageClauses),
tx.Visitor().VisitNodes(node.Members),
)
}
func (tx *MetadataTransformer) visitClassDeclaration(node *ast.ClassDeclaration) *ast.Node {
oldParent := tx.parent
tx.parent = node.AsNode()
defer tx.setParent(oldParent)
if !ast.ClassOrConstructorParameterIsDecorated(tx.legacyDecorators, node.AsNode()) {
return tx.Visitor().VisitEachChild(node.AsNode())
}
modifiers := tx.injectClassTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode())
return tx.Factory().UpdateClassDeclaration(
node,
modifiers,
tx.Visitor().VisitNode(node.Name()),
tx.Visitor().VisitNodes(node.TypeParameters),
tx.Visitor().VisitNodes(node.HeritageClauses),
tx.Visitor().VisitNodes(node.Members),
)
}
func (tx *MetadataTransformer) visitPropertyDeclaration(node *ast.PropertyDeclaration) *ast.Node {
if !ast.HasDecorators(node.AsNode()) {
return tx.Visitor().VisitEachChild(node.AsNode())
}
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
return tx.Factory().UpdatePropertyDeclaration(
node,
modifiers,
tx.Visitor().VisitNode(node.Name()),
tx.Visitor().VisitNode(node.PostfixToken),
tx.Visitor().VisitNode(node.Type),
tx.Visitor().VisitNode(node.Initializer),
)
}
func (tx *MetadataTransformer) visitMethodDeclaration(node *ast.MethodDeclaration) *ast.Node {
if !ast.HasDecorators(node.AsNode()) && len(getDecoratorsOfParameters(node.AsNode())) == 0 {
return tx.Visitor().VisitEachChild(node.AsNode())
}
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
return tx.Factory().UpdateMethodDeclaration(
node,
modifiers,
tx.Visitor().VisitNode(node.AsteriskToken),
tx.Visitor().VisitNode(node.Name()),
tx.Visitor().VisitNode(node.PostfixToken),
tx.Visitor().VisitNodes(node.TypeParameters),
tx.Visitor().VisitNodes(node.Parameters),
tx.Visitor().VisitNode(node.Type),
tx.Visitor().VisitNode(node.FullSignature),
tx.Visitor().VisitNode(node.Body),
)
}
func (tx *MetadataTransformer) visitSetAccessor(node *ast.SetAccessorDeclaration) *ast.Node {
if !ast.HasDecorators(node.AsNode()) && len(getDecoratorsOfParameters(node.AsNode())) == 0 {
return tx.Visitor().VisitEachChild(node.AsNode())
}
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
return tx.Factory().UpdateSetAccessorDeclaration(
node,
modifiers,
tx.Visitor().VisitNode(node.Name()),
tx.Visitor().VisitNodes(node.TypeParameters),
tx.Visitor().VisitNodes(node.Parameters),
tx.Visitor().VisitNode(node.Type),
tx.Visitor().VisitNode(node.FullSignature),
tx.Visitor().VisitNode(node.Body),
)
}
func (tx *MetadataTransformer) visitGetAccessor(node *ast.GetAccessorDeclaration) *ast.Node {
if !ast.HasDecorators(node.AsNode()) {
return tx.Visitor().VisitEachChild(node.AsNode())
}
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
return tx.Factory().UpdateGetAccessorDeclaration(
node,
modifiers,
tx.Visitor().VisitNode(node.Name()),
tx.Visitor().VisitNodes(node.TypeParameters),
tx.Visitor().VisitNodes(node.Parameters),
tx.Visitor().VisitNode(node.Type),
tx.Visitor().VisitNode(node.FullSignature),
tx.Visitor().VisitNode(node.Body),
)
}
func (tx *MetadataTransformer) injectClassTypeMetadata(list *ast.ModifierList, node *ast.Node) *ast.ModifierList {
metadata := tx.getTypeMetadata(node, node)
if len(metadata) > 0 {
var originalNodes []*ast.Node
if list != nil {
originalNodes = list.Nodes
}
if len(originalNodes) == 0 {
res := tx.Factory().NewModifierList(metadata)
if list != nil {
res.Loc = list.Loc
}
return res
}
var modifiersArray []*ast.Node
if ast.IsModifier(originalNodes[0]) && (originalNodes[0].Kind == ast.KindDefaultKeyword || originalNodes[0].Kind == ast.KindExportKeyword) {
modifiersArray = append(modifiersArray, originalNodes[0])
if len(originalNodes) > 1 && (originalNodes[1].Kind == ast.KindDefaultKeyword || originalNodes[1].Kind == ast.KindExportKeyword) {
modifiersArray = append(modifiersArray, originalNodes[1])
}
}
restStart := len(modifiersArray)
decos := core.Filter(originalNodes, ast.IsDecorator)
modifiersArray = append(modifiersArray, decos...)
modifiersArray = append(modifiersArray, metadata...)
otherModifiers := core.Filter(originalNodes[restStart:], ast.IsModifier)
modifiersArray = append(modifiersArray, otherModifiers...)
res := tx.Factory().NewModifierList(modifiersArray)
res.Loc = list.Loc
return res
}
return list
}
func (tx *MetadataTransformer) injectClassElementTypeMetadata(list *ast.ModifierList, node *ast.Node, container *ast.Node) *ast.ModifierList {
if !ast.IsClassLike(container) {
return list
}
if !ast.ClassElementOrClassElementParameterIsDecorated(tx.legacyDecorators, node, container) {
return list
}
metadata := tx.getTypeMetadata(node, container)
if len(metadata) > 0 {
var originalNodes []*ast.Node
if list != nil {
originalNodes = list.Nodes
}
if len(originalNodes) == 0 {
res := tx.Factory().NewModifierList(metadata)
if list != nil {
res.Loc = list.Loc
}
return res
}
var modifiersArray []*ast.Node
decos := core.Filter(originalNodes, ast.IsDecorator)
modifiersArray = append(modifiersArray, decos...)
modifiersArray = append(modifiersArray, metadata...)
modifiers := core.Filter(originalNodes, ast.IsModifier)
modifiersArray = append(modifiersArray, modifiers...)
res := tx.Factory().NewModifierList(modifiersArray)
res.Loc = list.Loc
return res
}
return list
}
/**
* Gets optional type metadata for a declaration.
*
* @param node The declaration node.
*/
func (tx *MetadataTransformer) getTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node {
// Decorator metadata is not yet supported for ES decorators.
if !tx.legacyDecorators {
return nil
}
if USE_NEW_TYPE_METADATA_FORMAT {
return tx.getNewTypeMetadata(node, container)
}
return tx.getOldTypeMetadata(node, container)
}
func (tx *MetadataTransformer) getOldTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node {
var decorators []*ast.Node
if tx.shouldAddTypeMetadata(node) {
typeMetadata := tx.Factory().NewMetadataHelper("design:type", tx.serializer.SerializeTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container))
decorators = append(decorators, tx.Factory().NewDecorator(typeMetadata))
}
if tx.shouldAddParamTypesMetadata(node) {
paramTypesMetadata := tx.Factory().NewMetadataHelper("design:paramtypes", tx.serializer.SerializeParameterTypesOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container))
decorators = append(decorators, tx.Factory().NewDecorator(paramTypesMetadata))
}
if tx.shouldAddReturnTypeMetadata(node) {
returnTypeMetadata := tx.Factory().NewMetadataHelper("design:returntype", tx.serializer.SerializeReturnTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node))
decorators = append(decorators, tx.Factory().NewDecorator(returnTypeMetadata))
}
return decorators
}
func (tx *MetadataTransformer) getNewTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node {
var properties []*ast.Node
if tx.shouldAddTypeMetadata(node) {
properties = append(properties, tx.Factory().NewPropertyAssignment(
nil,
tx.Factory().NewIdentifier("type"),
nil,
nil,
tx.Factory().NewArrowFunction(
nil,
nil,
tx.Factory().NewNodeList([]*ast.Node{}),
nil,
nil,
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken),
tx.serializer.SerializeTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container),
),
))
}
if tx.shouldAddParamTypesMetadata(node) {
properties = append(properties, tx.Factory().NewPropertyAssignment(
nil,
tx.Factory().NewIdentifier("paramTypes"),
nil,
nil,
tx.Factory().NewArrowFunction(
nil,
nil,
tx.Factory().NewNodeList([]*ast.Node{}),
nil,
nil,
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken),
tx.serializer.SerializeParameterTypesOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container),
),
))
}
if tx.shouldAddReturnTypeMetadata(node) {
properties = append(properties, tx.Factory().NewPropertyAssignment(
nil,
tx.Factory().NewIdentifier("returnType"),
nil,
nil,
tx.Factory().NewArrowFunction(
nil,
nil,
tx.Factory().NewNodeList([]*ast.Node{}),
nil,
nil,
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken),
tx.serializer.SerializeReturnTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node),
),
))
}
if len(properties) > 0 {
typeInfoMetadata := tx.Factory().NewMetadataHelper("design:typeinfo", tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList(properties), true))
return []*ast.Node{tx.Factory().NewDecorator(typeInfoMetadata)}
}
return nil
}
/**
* Determines whether to emit the "design:type" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
func (tx *MetadataTransformer) shouldAddTypeMetadata(node *ast.Node) bool {
switch node.Kind {
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration:
return true
}
return false
}
/**
* Determines whether to emit the "design:returntype" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
func (tx *MetadataTransformer) shouldAddReturnTypeMetadata(node *ast.Node) bool {
return node.Kind == ast.KindMethodDeclaration
}
/**
* Determines whether to emit the "design:paramtypes" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
func (tx *MetadataTransformer) shouldAddParamTypesMetadata(node *ast.Node) bool {
switch node.Kind {
case ast.KindClassDeclaration, ast.KindClassExpression:
return ast.GetFirstConstructorWithBody(node) != nil
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
return true
}
return false
}

View File

@@ -0,0 +1,995 @@
package tstransforms
// !!! SourceMaps and Comments need to be validated
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/jsnum"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
// Transforms TypeScript-specific runtime syntax into JavaScript-compatible syntax.
type RuntimeSyntaxTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
parentNode *ast.Node
currentNode *ast.Node
currentSourceFile *ast.Node
currentScope *ast.Node // SourceFile | Block | ModuleBlock | CaseBlock
currentScopeFirstDeclarationsOfName map[string]*ast.Node
currentEnum *ast.EnumDeclarationNode
currentNamespace *ast.ModuleDeclarationNode
resolver binder.ReferenceResolver
emitResolver printer.EmitResolver
}
func NewRuntimeSyntaxTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opt.CompilerOptions
emitContext := opt.Context
tx := &RuntimeSyntaxTransformer{compilerOptions: compilerOptions, resolver: opt.Resolver, emitResolver: opt.EmitResolver}
return tx.NewTransformer(tx.visit, emitContext)
}
// Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`.
func (tx *RuntimeSyntaxTransformer) pushNode(node *ast.Node) (grandparentNode *ast.Node) {
grandparentNode = tx.parentNode
tx.parentNode = tx.currentNode
tx.currentNode = node
return grandparentNode
}
// Pops the last child node off the ancestor tracking stack, restoring the grandparent node.
func (tx *RuntimeSyntaxTransformer) popNode(grandparentNode *ast.Node) {
tx.currentNode = tx.parentNode
tx.parentNode = grandparentNode
}
func (tx *RuntimeSyntaxTransformer) pushScope(node *ast.Node) (savedCurrentScope *ast.Node, savedCurrentScopeFirstDeclarationsOfName map[string]*ast.Node) {
savedCurrentScope = tx.currentScope
savedCurrentScopeFirstDeclarationsOfName = tx.currentScopeFirstDeclarationsOfName
switch node.Kind {
case ast.KindSourceFile:
tx.currentScope = node
tx.currentSourceFile = node
tx.currentScopeFirstDeclarationsOfName = nil
case ast.KindCaseBlock, ast.KindModuleBlock, ast.KindBlock:
tx.currentScope = node
tx.currentScopeFirstDeclarationsOfName = nil
case ast.KindFunctionDeclaration, ast.KindClassDeclaration, ast.KindVariableStatement:
tx.recordDeclarationInScope(node)
}
return savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName
}
func (tx *RuntimeSyntaxTransformer) popScope(savedCurrentScope *ast.Node, savedCurrentScopeFirstDeclarationsOfName map[string]*ast.Node) {
if tx.currentScope != savedCurrentScope {
// only reset the first declaration for a name if we are exiting the scope in which it was declared
tx.currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName
}
tx.currentScope = savedCurrentScope
}
// Visits each node in the AST
func (tx *RuntimeSyntaxTransformer) visit(node *ast.Node) *ast.Node {
grandparentNode := tx.pushNode(node)
defer tx.popNode(grandparentNode)
savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName := tx.pushScope(node)
defer tx.popScope(savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName)
if node.SubtreeFacts()&ast.SubtreeContainsTypeScript == 0 && (tx.currentNamespace == nil && tx.currentEnum == nil || node.SubtreeFacts()&ast.SubtreeContainsIdentifier == 0) {
return node
}
switch node.Kind {
// TypeScript parameter property modifiers are elided
case ast.KindPublicKeyword,
ast.KindPrivateKeyword,
ast.KindProtectedKeyword,
ast.KindReadonlyKeyword,
ast.KindOverrideKeyword:
node = nil
case ast.KindEnumDeclaration:
node = tx.visitEnumDeclaration(node.AsEnumDeclaration())
case ast.KindModuleDeclaration:
node = tx.visitModuleDeclaration(node.AsModuleDeclaration())
case ast.KindClassDeclaration:
node = tx.visitClassDeclaration(node.AsClassDeclaration())
case ast.KindClassExpression:
node = tx.visitClassExpression(node.AsClassExpression())
case ast.KindConstructor:
node = tx.visitConstructorDeclaration(node.AsConstructorDeclaration())
case ast.KindFunctionDeclaration:
node = tx.visitFunctionDeclaration(node.AsFunctionDeclaration())
case ast.KindVariableStatement:
node = tx.visitVariableStatement(node.AsVariableStatement())
case ast.KindExportDeclaration, ast.KindImportDeclaration, ast.KindImportClause:
if tx.currentNamespace != nil && tx.currentScope != nil && tx.currentScope.Kind != ast.KindBlock {
// do not emit ES6 imports and exports since they are illegal inside a namespace
node = nil
} else {
node = tx.Visitor().VisitEachChild(node)
}
case ast.KindImportEqualsDeclaration:
if tx.currentNamespace != nil && tx.currentScope != nil && tx.currentScope.Kind != ast.KindBlock && node.AsImportEqualsDeclaration().ModuleReference.Kind == ast.KindExternalModuleReference {
// do not emit ES6 imports and exports since they are illegal inside a namespace
node = nil
} else if tx.currentNamespace != nil && tx.currentScope != nil && tx.currentScope.Kind == ast.KindBlock && node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference {
// inside a block within a namespace, elide internal import aliases
node = nil
} else {
node = tx.visitImportEqualsDeclaration(node.AsImportEqualsDeclaration())
}
case ast.KindIdentifier:
node = tx.visitIdentifier(node)
case ast.KindShorthandPropertyAssignment:
node = tx.visitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
default:
node = tx.Visitor().VisitEachChild(node)
}
return node
}
// Records that a declaration was emitted in the current scope, if it was the first declaration for the provided symbol.
func (tx *RuntimeSyntaxTransformer) recordDeclarationInScope(node *ast.Node) {
switch node.Kind {
case ast.KindVariableStatement:
tx.recordDeclarationInScope(node.AsVariableStatement().DeclarationList)
return
case ast.KindVariableDeclarationList:
for _, decl := range node.AsVariableDeclarationList().Declarations.Nodes {
tx.recordDeclarationInScope(decl)
}
return
case ast.KindArrayBindingPattern, ast.KindObjectBindingPattern:
for _, element := range node.Elements() {
tx.recordDeclarationInScope(element)
}
return
}
name := node.Name()
if name != nil {
if ast.IsIdentifier(name) {
if tx.currentScopeFirstDeclarationsOfName == nil {
tx.currentScopeFirstDeclarationsOfName = make(map[string]*ast.Node)
}
text := name.Text()
if _, found := tx.currentScopeFirstDeclarationsOfName[text]; !found {
tx.currentScopeFirstDeclarationsOfName[text] = node
}
} else if ast.IsBindingPattern(name) {
tx.recordDeclarationInScope(name)
}
}
}
// Determines whether a declaration is the first declaration with the same name emitted in the current scope.
func (tx *RuntimeSyntaxTransformer) isFirstDeclarationInScope(node *ast.Node) bool {
name := node.Name()
if name != nil && ast.IsIdentifier(name) {
text := name.Text()
if firstDeclaration, found := tx.currentScopeFirstDeclarationsOfName[text]; found {
return firstDeclaration == node
}
}
return false
}
func (tx *RuntimeSyntaxTransformer) isExportOfNamespace(node *ast.Node) bool {
return tx.currentNamespace != nil && (tx.currentScope == nil || tx.currentScope.Kind != ast.KindBlock) && node.ModifierFlags()&ast.ModifierFlagsExport != 0
}
// Gets an expression that represents a property name, such as `"foo"` for the identifier `foo`.
func (tx *RuntimeSyntaxTransformer) getExpressionForPropertyName(member *ast.EnumMember) *ast.Expression {
name := member.Name()
switch name.Kind {
case ast.KindPrivateIdentifier:
return tx.Factory().NewIdentifier("")
case ast.KindComputedPropertyName:
n := name.AsComputedPropertyName()
// enums don't support computed properties so we always generate the 'expression' part of the name as-is.
return tx.Visitor().VisitNode(n.Expression)
case ast.KindIdentifier:
return tx.Factory().NewStringLiteral(name.Text(), ast.TokenFlagsNone)
case ast.KindStringLiteral: // !!! propagate token flags (will produce new diffs)
return tx.Factory().NewStringLiteral(name.Text(), ast.TokenFlagsNone)
case ast.KindNumericLiteral:
return tx.Factory().NewNumericLiteral(name.Text(), ast.TokenFlagsNone)
default:
return name
}
}
// Gets an expression like `E["A"]` that references an enum member.
func (tx *RuntimeSyntaxTransformer) getEnumQualifiedElement(enum *ast.EnumDeclaration, member *ast.EnumMember) *ast.Expression {
prop := tx.getNamespaceQualifiedElement(tx.getNamespaceContainerName(enum.AsNode()), tx.getExpressionForPropertyName(member))
tx.EmitContext().AddEmitFlags(prop, printer.EFNoComments|printer.EFNoNestedComments|printer.EFNoSourceMap|printer.EFNoNestedSourceMaps)
return prop
}
// Gets an expression used to refer to a namespace or enum from within the body of its declaration.
func (tx *RuntimeSyntaxTransformer) getNamespaceContainerName(node *ast.Node) *ast.IdentifierNode {
return tx.Factory().NewGeneratedNameForNode(node)
}
// Gets an expression used to refer to an export of a namespace or a member of an enum by property name.
func (tx *RuntimeSyntaxTransformer) getNamespaceQualifiedProperty(ns *ast.IdentifierNode, name *ast.IdentifierNode) *ast.Expression {
return tx.Factory().GetNamespaceMemberName(ns, name, printer.NameOptions{AllowSourceMaps: true})
}
// Gets an expression used to refer to an export of a namespace or a member of an enum by indexed access.
func (tx *RuntimeSyntaxTransformer) getNamespaceQualifiedElement(ns *ast.IdentifierNode, expression *ast.Expression) *ast.Expression {
qualifiedName := tx.EmitContext().Factory.NewElementAccessExpression(ns, nil /*questionDotToken*/, expression, ast.NodeFlagsNone)
tx.EmitContext().AssignCommentAndSourceMapRanges(qualifiedName, expression)
return qualifiedName
}
// Gets an expression used within the provided node's container for any exported references.
func (tx *RuntimeSyntaxTransformer) getExportQualifiedReferenceToDeclaration(node *ast.Declaration) *ast.Expression {
if tx.isExportOfNamespace(node.AsNode()) {
return tx.Factory().GetExternalModuleOrNamespaceExportName(tx.getNamespaceContainerName(tx.currentNamespace), node, false /*allowComments*/, true /*allowSourceMaps*/)
}
return tx.Factory().GetDeclarationNameEx(node.AsNode(), printer.NameOptions{AllowSourceMaps: true})
}
func (tx *RuntimeSyntaxTransformer) addVarForDeclaration(statements []*ast.Statement, node *ast.Declaration) ([]*ast.Statement, bool) {
tx.recordDeclarationInScope(node)
if !tx.isFirstDeclarationInScope(node) {
return statements, false
}
// var name;
name := tx.Factory().GetLocalNameEx(node, printer.AssignedNameOptions{AllowSourceMaps: true})
varDecl := tx.Factory().NewVariableDeclaration(name, nil, nil, nil)
varFlags := core.IfElse(tx.currentScope == tx.currentSourceFile, ast.NodeFlagsNone, ast.NodeFlagsLet)
varDecls := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{varDecl}), varFlags)
// Replicate modifierVisitor: strip decorators, TypeScript modifiers, and export when in namespace.
modifierMask := ^(ast.ModifierFlagsTypeScriptModifier | ast.ModifierFlagsDecorator)
if tx.currentNamespace != nil {
modifierMask &^= ast.ModifierFlagsExport
}
modifiers := transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), modifierMask)
varStatement := tx.Factory().NewVariableStatement(modifiers, varDecls)
tx.EmitContext().SetOriginal(varDecl, node)
// !!! synthetic comments
tx.EmitContext().SetOriginal(varStatement, node)
// Adjust the source map emit to match the old emitter.
if ast.IsEnumDeclaration(node) {
tx.EmitContext().SetSourceMapRange(varDecls, node.Loc)
} else {
tx.EmitContext().SetSourceMapRange(varStatement, node.Loc)
}
// Trailing comments for enum declaration should be emitted after the function closure
// instead of the variable statement:
//
// /** Leading comment*/
// enum E {
// A
// } // trailing comment
//
// Should emit:
//
// /** Leading comment*/
// var E;
// (function (E) {
// E[E["A"] = 0] = "A";
// })(E || (E = {})); // trailing comment
//
tx.EmitContext().SetCommentRange(varStatement, node.Loc)
tx.EmitContext().AddEmitFlags(varStatement, printer.EFNoTrailingComments)
statements = append(statements, varStatement)
return statements, true
}
func (tx *RuntimeSyntaxTransformer) visitEnumDeclaration(node *ast.EnumDeclaration) *ast.Node {
if !tx.shouldEmitEnumDeclaration(node) {
return tx.EmitContext().NewNotEmittedStatement(node.AsNode())
}
statements := []*ast.Statement{}
// If needed, we should emit a variable declaration for the enum:
// var name;
statements, varAdded := tx.addVarForDeclaration(statements, node.AsNode())
// If we emit a leading variable declaration, we should not emit leading comments for the enum body, but we should
// still emit the comments if we are emitting to a System module.
emitFlags := printer.EFNone
if varAdded && (tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindSystem || tx.currentScope != tx.currentSourceFile) {
emitFlags |= printer.EFNoLeadingComments
}
// x || (x = {})
// exports.x || (exports.x = {})
enumArg := tx.Factory().NewLogicalORExpression(
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
tx.Factory().NewAssignmentExpression(
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{}), false),
),
)
if tx.isExportOfNamespace(node.AsNode()) {
// `localName` is the expression used within this node's containing scope for any local references.
localName := tx.Factory().GetLocalNameEx(node.AsNode(), printer.AssignedNameOptions{AllowSourceMaps: true})
// x = (exports.x || (exports.x = {}))
enumArg = tx.Factory().NewAssignmentExpression(localName, enumArg)
}
// (function (name) { ... })(name || (name = {}))
enumParamName := tx.Factory().NewGeneratedNameForNode(node.AsNode())
tx.EmitContext().SetSourceMapRange(enumParamName, node.Name().Loc)
enumParam := tx.Factory().NewParameterDeclaration(nil, nil, enumParamName, nil, nil, nil)
enumBody := tx.transformEnumBody(node)
enumFunc := tx.Factory().NewFunctionExpression(nil, nil, nil, nil, tx.Factory().NewNodeList([]*ast.Node{enumParam}), nil, nil, enumBody)
enumCall := tx.Factory().NewCallExpression(tx.Factory().NewParenthesizedExpression(enumFunc), nil, nil, tx.Factory().NewNodeList([]*ast.Node{enumArg}), ast.NodeFlagsNone)
enumStatement := tx.Factory().NewExpressionStatement(enumCall)
tx.EmitContext().SetOriginal(enumStatement, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(enumStatement, node.AsNode())
tx.EmitContext().AddEmitFlags(enumStatement, emitFlags)
return tx.Factory().NewSyntaxList(append(statements, enumStatement))
}
// Transforms the body of an enum declaration.
func (tx *RuntimeSyntaxTransformer) transformEnumBody(node *ast.EnumDeclaration) *ast.BlockNode {
savedCurrentEnum := tx.currentEnum
tx.currentEnum = node.AsNode()
// visit the children of `node` in advance to capture any references to enum members
node = tx.Visitor().VisitEachChild(node.AsNode()).AsEnumDeclaration()
statements := []*ast.Statement{}
for i := range len(node.Members.Nodes) {
// E[E["A"] = 0] = "A";
statements = tx.transformEnumMember(
statements,
node,
i,
)
}
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Members.Loc
tx.currentEnum = savedCurrentEnum
return tx.Factory().NewBlock(statementList, true /*multiline*/)
}
// Transforms an enum member into a statement. It is expected that `enum` has already been visited.
func (tx *RuntimeSyntaxTransformer) transformEnumMember(
statements []*ast.Statement,
enum *ast.EnumDeclaration,
index int,
) []*ast.Statement {
memberNode := enum.Members.Nodes[index]
member := memberNode.AsEnumMember()
savedParent := tx.parentNode
tx.parentNode = tx.currentNode
tx.currentNode = memberNode
// E[E["A"] = x] = "A";
// ^
expression := member.Initializer // NOTE: already visited
var useExplicitReverseMapping bool
parseNode := tx.EmitContext().ParseNode(memberNode)
result := tx.emitResolver.GetEnumMemberValue(parseNode)
switch value := result.Value.(type) {
case jsnum.Number:
expression = core.Coalesce(constantExpression(value, tx.Factory()), expression)
useExplicitReverseMapping = true
case string:
expression = core.Coalesce(constantExpression(value, tx.Factory()), expression)
default:
if expression == nil {
expression = tx.Factory().NewVoidZeroExpression()
}
useExplicitReverseMapping = !result.IsSyntacticallyString
}
// Define the enum member property:
// E[E["A"] = 0] = "A";
// ^^^^^^^^--_____
expression = tx.Factory().NewAssignmentExpression(
tx.getEnumQualifiedElement(enum, member),
expression,
)
if useExplicitReverseMapping {
// E[E["A"] = 0] = "A";
// ^^--------------^^^^^
expression = tx.Factory().NewAssignmentExpression(
tx.Factory().NewElementAccessExpression(
tx.getNamespaceContainerName(enum.AsNode()),
nil, /*questionDotToken*/
expression,
ast.NodeFlagsNone,
),
tx.getExpressionForPropertyName(member),
)
}
memberStatement := tx.Factory().NewExpressionStatement(expression)
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, member.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(memberStatement, member.AsNode())
statements = append(statements, memberStatement)
tx.currentNode = tx.parentNode
tx.parentNode = savedParent
return statements
}
func (tx *RuntimeSyntaxTransformer) visitModuleDeclaration(node *ast.ModuleDeclaration) *ast.Node {
if !tx.shouldEmitModuleDeclaration(node) {
return tx.EmitContext().NewNotEmittedStatement(node.AsNode())
}
statements := []*ast.Statement{}
// If needed, we should emit a variable declaration for the module:
// var name;
statements, varAdded := tx.addVarForDeclaration(statements, node.AsNode())
// If we emit a leading variable declaration, we should not emit leading comments for the module body, but we should
// still emit the comments if we are emitting to a System module.
emitFlags := printer.EFNone
if varAdded && (tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindSystem || tx.currentScope != tx.currentSourceFile) {
emitFlags |= printer.EFNoLeadingComments
}
// x || (x = {})
// exports.x || (exports.x = {})
moduleArg := tx.Factory().NewLogicalORExpression(
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
tx.Factory().NewAssignmentExpression(
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{}), false),
),
)
if tx.isExportOfNamespace(node.AsNode()) {
// `localName` is the expression used within this node's containing scope for any local references.
localName := tx.Factory().GetLocalNameEx(node.AsNode(), printer.AssignedNameOptions{AllowSourceMaps: true})
// x = (exports.x || (exports.x = {}))
moduleArg = tx.Factory().NewAssignmentExpression(localName, moduleArg)
}
// (function (name) { ... })(name || (name = {}))
moduleParamName := tx.Factory().NewGeneratedNameForNode(node.AsNode())
tx.EmitContext().SetSourceMapRange(moduleParamName, node.Name().Loc)
moduleParam := tx.Factory().NewParameterDeclaration(nil, nil, moduleParamName, nil, nil, nil)
moduleBody := tx.transformModuleBody(node, tx.getNamespaceContainerName(node.AsNode()))
moduleFunc := tx.Factory().NewFunctionExpression(nil, nil, nil, nil, tx.Factory().NewNodeList([]*ast.Node{moduleParam}), nil, nil, moduleBody)
moduleCall := tx.Factory().NewCallExpression(tx.Factory().NewParenthesizedExpression(moduleFunc), nil, nil, tx.Factory().NewNodeList([]*ast.Node{moduleArg}), ast.NodeFlagsNone)
moduleStatement := tx.Factory().NewExpressionStatement(moduleCall)
tx.EmitContext().SetOriginal(moduleStatement, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(moduleStatement, node.AsNode())
tx.EmitContext().AddEmitFlags(moduleStatement, emitFlags)
return tx.Factory().NewSyntaxList(append(statements, moduleStatement))
}
func (tx *RuntimeSyntaxTransformer) transformModuleBody(node *ast.ModuleDeclaration, namespaceLocalName *ast.IdentifierNode) *ast.BlockNode {
savedCurrentNamespace := tx.currentNamespace
savedCurrentScope := tx.currentScope
savedCurrentScopeFirstDeclarationsOfName := tx.currentScopeFirstDeclarationsOfName
tx.currentNamespace = node.AsNode()
tx.currentScopeFirstDeclarationsOfName = nil
var statements []*ast.Statement
tx.EmitContext().StartVariableEnvironment()
var statementsLocation core.TextRange
var blockLocation core.TextRange
if node.Body != nil {
if node.Body.Kind == ast.KindModuleBlock {
// visit the children of `node` in advance to capture any references to namespace members
node = tx.Visitor().VisitEachChild(node.AsNode()).AsModuleDeclaration()
body := node.Body.AsModuleBlock()
statements = body.Statements.Nodes
statementsLocation = body.Statements.Loc
blockLocation = body.Loc
} else { // node.Body.Kind == ast.KindModuleDeclaration
// !!! Strada didn't do this; why?
// tx.currentScope = node.AsNode()
statements, _ = tx.Visitor().VisitSlice([]*ast.Node{node.Body})
moduleBlock := getInnermostModuleDeclarationFromDottedModule(node).Body.AsModuleBlock()
statementsLocation = moduleBlock.Statements.Loc.WithPos(-1)
}
}
tx.currentNamespace = savedCurrentNamespace
tx.currentScope = savedCurrentScope
tx.currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName
statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = statementsLocation
block := tx.Factory().NewBlock(statementList, true /*multiline*/)
block.Loc = blockLocation
// namespace hello.hi.world {
// function foo() {}
//
// // TODO, blah
// }
//
// should be emitted as
//
// var hello;
// (function (hello) {
// var hi;
// (function (hi) {
// var world;
// (function (world) {
// function foo() { }
// // TODO, blah
// })(world = hi.world || (hi.world = {}));
// })(hi = hello.hi || (hello.hi = {}));
// })(hello || (hello = {}));
//
// We only want to emit comment on the namespace which contains block body itself, not the containing namespaces.
if node.Body == nil || node.Body.Kind != ast.KindModuleBlock {
tx.EmitContext().AddEmitFlags(block, printer.EFNoComments)
}
return block
}
func (tx *RuntimeSyntaxTransformer) visitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) *ast.Node {
if node.ModuleReference.Kind == ast.KindExternalModuleReference {
return tx.Visitor().VisitEachChild(node.AsNode())
}
moduleReference := tx.Factory().CreateExpressionFromEntityName(node.ModuleReference)
tx.EmitContext().SetEmitFlags(moduleReference, printer.EFNoComments|printer.EFNoNestedComments)
if !tx.isExportOfNamespace(node.AsNode()) {
// export var ${name} = ${moduleReference};
// var ${name} = ${moduleReference};
varDecl := tx.Factory().NewVariableDeclaration(node.Name(), nil /*exclamationToken*/, nil /*type*/, moduleReference)
tx.EmitContext().SetOriginal(varDecl, node.AsNode())
varList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsNone)
varModifiers := transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ast.ModifierFlagsExport)
varStatement := tx.Factory().NewVariableStatement(varModifiers, varList)
tx.EmitContext().SetOriginal(varStatement, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode())
return varStatement
} else {
// exports.${name} = ${moduleReference};
statement := tx.createExportStatement(node.Name(), moduleReference, node.Loc, node.Loc, node.AsNode())
statement.Loc = node.Loc
return statement
}
}
func (tx *RuntimeSyntaxTransformer) visitVariableStatement(node *ast.VariableStatement) *ast.Node {
if tx.isExportOfNamespace(node.AsNode()) {
expressions := []*ast.Expression{}
for _, declaration := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
v := declaration.AsVariableDeclaration()
if v.Initializer == nil {
continue
}
if ast.IsBindingPattern(v.Name()) {
expression := transformers.FlattenDestructuringAssignment(
&tx.Transformer,
tx.Visitor().VisitNode(declaration),
false, /*needsValue*/
transformers.FlattenLevelAll,
tx.createNamespaceExportExpression,
)
if expression != nil {
expressions = append(expressions, expression)
}
} else {
expression := transformers.ConvertVariableDeclarationToAssignmentExpression(tx.EmitContext(), v)
if expression != nil {
expressions = append(expressions, expression)
}
}
}
if len(expressions) == 0 {
return nil
}
expression := tx.Factory().InlineExpressions(expressions)
statement := tx.Factory().NewExpressionStatement(expression)
tx.EmitContext().SetOriginal(statement, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
// re-visit as the new node
savedCurrent := tx.currentNode
tx.currentNode = statement
statement = tx.Visitor().VisitEachChild(statement)
tx.currentNode = savedCurrent
return statement
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
// createNamespaceExportExpression creates an assignment to a namespace member for use as a
// callback during destructuring flattening.
func (tx *RuntimeSyntaxTransformer) createNamespaceExportExpression(exportName *ast.IdentifierNode, exportValue *ast.Expression, location *core.TextRange) *ast.Expression {
memberName := tx.getNamespaceQualifiedProperty(tx.getNamespaceContainerName(tx.currentNamespace), exportName)
expression := tx.Factory().NewAssignmentExpression(memberName, exportValue)
if location != nil {
expression.Loc = *location
}
return expression
}
func (tx *RuntimeSyntaxTransformer) visitFunctionDeclaration(node *ast.FunctionDeclaration) *ast.Node {
if tx.isExportOfNamespace(node.AsNode()) {
updated := tx.Factory().UpdateFunctionDeclaration(
node,
tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExport)),
node.AsteriskToken,
tx.Visitor().VisitNode(node.Name()),
nil, /*typeParameters*/
tx.Visitor().VisitNodes(node.Parameters),
nil, /*returnType*/
nil, /*fullSignature*/
tx.Visitor().VisitNode(node.Body),
)
export := tx.createExportStatementForDeclaration(node.AsNode())
if export != nil {
return tx.Factory().NewSyntaxList([]*ast.Node{updated, export})
}
return updated
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *RuntimeSyntaxTransformer) getParameterProperties(constructor *ast.Node) []*ast.ParameterDeclaration {
var parameterProperties []*ast.ParameterDeclaration
if constructor != nil {
for _, parameter := range constructor.Parameters() {
if ast.IsParameterPropertyDeclaration(parameter, constructor) {
parameterProperties = append(parameterProperties, parameter.AsParameterDeclaration())
}
}
}
return parameterProperties
}
func (tx *RuntimeSyntaxTransformer) visitClassDeclaration(node *ast.ClassDeclaration) *ast.Node {
exported := tx.isExportOfNamespace(node.AsNode())
var modifiers *ast.ModifierList
if exported {
modifiers = tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault))
} else {
modifiers = tx.Visitor().VisitModifiers(node.Modifiers())
}
name := tx.Visitor().VisitNode(node.Name())
if name == nil && (exported || ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil)) {
name = tx.Factory().NewGeneratedNameForNode(node.AsNode())
}
heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses)
members := tx.Visitor().VisitNodes(node.Members)
parameterProperties := tx.getParameterProperties(core.Find(node.Members.Nodes, ast.IsConstructorDeclaration))
if len(parameterProperties) > 0 {
var newMembers []*ast.ClassElement
for _, parameter := range parameterProperties {
if ast.IsIdentifier(parameter.Name()) {
parameterProperty := tx.Factory().NewPropertyDeclaration(
nil, /*modifiers*/
parameter.Name().Clone(tx.Factory()),
nil, /*questionOrExclamationToken*/
nil, /*type*/
nil, /*initializer*/
)
tx.EmitContext().SetOriginal(parameterProperty, parameter.AsNode())
newMembers = append(newMembers, parameterProperty)
}
}
if len(newMembers) > 0 {
newMembers = append(newMembers, members.Nodes...)
members = tx.Factory().NewNodeList(newMembers)
members.Loc = node.Members.Loc
}
}
updated := tx.Factory().UpdateClassDeclaration(node, modifiers, name, nil /*typeParameters*/, heritageClauses, members)
if exported {
export := tx.createExportStatementForDeclaration(node.AsNode())
if export != nil {
return tx.Factory().NewSyntaxList([]*ast.Node{updated, export})
}
}
return updated
}
func (tx *RuntimeSyntaxTransformer) visitClassExpression(node *ast.ClassExpression) *ast.Node {
modifiers := tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault))
name := tx.Visitor().VisitNode(node.Name())
heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses)
members := tx.Visitor().VisitNodes(node.Members)
parameterProperties := tx.getParameterProperties(core.Find(node.Members.Nodes, ast.IsConstructorDeclaration))
if len(parameterProperties) > 0 {
var newMembers []*ast.ClassElement
for _, parameter := range parameterProperties {
if ast.IsIdentifier(parameter.Name()) {
parameterProperty := tx.Factory().NewPropertyDeclaration(
nil, /*modifiers*/
parameter.Name().Clone(tx.Factory()),
nil, /*questionOrExclamationToken*/
nil, /*type*/
nil, /*initializer*/
)
tx.EmitContext().SetOriginal(parameterProperty, parameter.AsNode())
newMembers = append(newMembers, parameterProperty)
}
}
if len(newMembers) > 0 {
newMembers = append(newMembers, members.Nodes...)
members = tx.Factory().NewNodeList(newMembers)
members.Loc = node.Members.Loc
}
}
return tx.Factory().UpdateClassExpression(node, modifiers, name, nil /*typeParameters*/, heritageClauses, members)
}
func (tx *RuntimeSyntaxTransformer) visitConstructorDeclaration(node *ast.ConstructorDeclaration) *ast.Node {
modifiers := tx.Visitor().VisitModifiers(node.Modifiers())
parameters := tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
body := tx.visitConstructorBody(node.Body.AsBlock(), node.AsNode())
return tx.Factory().UpdateConstructorDeclaration(node, modifiers, nil /*typeParameters*/, parameters, nil /*returnType*/, nil /*fullSignature*/, body)
}
func (tx *RuntimeSyntaxTransformer) visitConstructorBody(body *ast.Block, constructor *ast.Node) *ast.Node {
parameterProperties := tx.getParameterProperties(constructor)
if len(parameterProperties) == 0 {
return tx.EmitContext().VisitFunctionBody(body.AsNode(), tx.Visitor())
}
grandparentOfBody := tx.pushNode(body.AsNode())
savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName := tx.pushScope(body.AsNode())
tx.EmitContext().StartVariableEnvironment()
prologue, rest := tx.Factory().SplitStandardPrologue(body.Statements.Nodes)
statements := slices.Clone(prologue)
// Transform parameters into property assignments. Transforms this:
//
// constructor (public x, public y) {
// }
//
// Into this:
//
// constructor (x, y) {
// this.x = x;
// this.y = y;
// }
//
var parameterPropertyAssignments []*ast.Statement
for _, parameter := range parameterProperties {
if ast.IsIdentifier(parameter.Name()) {
propertyName := parameter.Name().Clone(tx.Factory())
propertyName.Parent = parameter.Name().Parent //nolint:customlint // .Parent set to get node to printback using text from original file instead of processed text; TODO: this should be achievable via EmitFlags instead
tx.EmitContext().AddEmitFlags(propertyName, printer.EFNoComments|printer.EFNoSourceMap)
localName := parameter.Name().Clone(tx.Factory())
localName.Parent = parameter.Name().Parent //nolint:customlint // .Parent set to get node to printback using text from original file instead of processed text; TODO: this should be achievable via EmitFlags instead
tx.EmitContext().AddEmitFlags(localName, printer.EFNoComments)
parameterProperty := tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(
tx.Factory().NewThisExpression(),
nil, /*questionDotToken*/
propertyName,
ast.NodeFlagsNone,
),
localName,
),
)
tx.EmitContext().SetOriginal(parameterProperty, parameter.AsNode())
tx.EmitContext().AddEmitFlags(parameterProperty, printer.EFStartOnNewLine)
parameterPropertyAssignments = append(parameterPropertyAssignments, parameterProperty)
}
}
superPath := transformers.FindSuperStatementIndexPath(rest, 0)
if len(superPath) > 0 {
statements = append(statements, tx.transformConstructorBodyWorker(rest, superPath, parameterPropertyAssignments)...)
} else {
statements = append(statements, parameterPropertyAssignments...)
statements = append(statements, core.FirstResult(tx.Visitor().VisitSlice(rest))...)
}
statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = body.Statements.Loc
tx.popScope(savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName)
tx.popNode(grandparentOfBody)
updated := tx.Factory().NewBlock(statementList /*multiline*/, true)
tx.EmitContext().SetOriginal(updated, body.AsNode())
updated.Loc = body.Loc
return updated
}
func (tx *RuntimeSyntaxTransformer) transformConstructorBodyWorker(statementsIn []*ast.Statement, superPath []int, initializerStatements []*ast.Statement) []*ast.Statement {
var statementsOut []*ast.Statement
superStatementIndex := superPath[0]
superStatement := statementsIn[superStatementIndex]
// visit up to the statement containing `super`
statementsOut = append(statementsOut, core.FirstResult(tx.Visitor().VisitSlice(statementsIn[:superStatementIndex]))...)
// if the statement containing `super` is a `try` statement, transform the body of the `try` block
if ast.IsTryStatement(superStatement) {
tryStatement := superStatement.AsTryStatement()
tryBlock := tryStatement.TryBlock.AsBlock()
// keep track of hierarchy as we descend
grandparentOfTryStatement := tx.pushNode(tryStatement.AsNode())
grandparentOfTryBlock := tx.pushNode(tryBlock.AsNode())
savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName := tx.pushScope(tryBlock.AsNode())
// visit the `try` block
tryBlockStatements := tx.transformConstructorBodyWorker(
tryBlock.Statements.Nodes,
superPath[1:],
initializerStatements,
)
// restore hierarchy as we ascend to the `try` statement
tx.popScope(savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName)
tx.popNode(grandparentOfTryBlock)
tryBlockStatementList := tx.Factory().NewNodeList(tryBlockStatements)
tryBlockStatementList.Loc = tryBlock.Statements.Loc
statementsOut = append(statementsOut, tx.Factory().UpdateTryStatement(
tryStatement,
tx.Factory().UpdateBlock(tryBlock, tryBlockStatementList, tryBlock.MultiLine),
tx.Visitor().VisitNode(tryStatement.CatchClause),
tx.Visitor().VisitNode(tryStatement.FinallyBlock),
))
// restore hierarchy as we ascend to the parent of the `try` statement
tx.popNode(grandparentOfTryStatement)
} else {
// visit the statement containing `super`
statementsOut = append(statementsOut, core.FirstResult(tx.Visitor().VisitSlice(statementsIn[superStatementIndex:superStatementIndex+1]))...)
// insert the initializer statements
statementsOut = append(statementsOut, initializerStatements...)
}
// visit the statements after `super`
statementsOut = append(statementsOut, core.FirstResult(tx.Visitor().VisitSlice(statementsIn[superStatementIndex+1:]))...)
return statementsOut
}
func (tx *RuntimeSyntaxTransformer) visitShorthandPropertyAssignment(node *ast.ShorthandPropertyAssignment) *ast.Node {
name := node.Name()
exportedOrImportedName := tx.visitExpressionIdentifier(name)
if exportedOrImportedName != name {
expression := exportedOrImportedName
if node.ObjectAssignmentInitializer != nil {
equalsToken := node.EqualsToken
if equalsToken == nil {
equalsToken = tx.Factory().NewToken(ast.KindEqualsToken)
}
expression = tx.Factory().NewBinaryExpression(
nil, /*modifiers*/
expression,
nil, /*typeNode*/
equalsToken,
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
)
}
updated := tx.Factory().NewPropertyAssignment(nil /*modifiers*/, node.Name(), nil /*postfixToken*/, nil /*typeNode*/, expression)
updated.Loc = node.Loc
tx.EmitContext().SetOriginal(updated, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(updated, node.AsNode())
return updated
}
return tx.Factory().UpdateShorthandPropertyAssignment(
node,
nil, /*modifiers*/
exportedOrImportedName,
nil, /*postfixToken*/
nil, /*typeNode*/
node.EqualsToken,
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
)
}
func (tx *RuntimeSyntaxTransformer) visitIdentifier(node *ast.IdentifierNode) *ast.Node {
if transformers.IsIdentifierReference(node, tx.parentNode) {
return tx.visitExpressionIdentifier(node)
}
return node
}
func (tx *RuntimeSyntaxTransformer) visitExpressionIdentifier(node *ast.IdentifierNode) *ast.Node {
if (tx.currentEnum != nil || tx.currentNamespace != nil) && !transformers.IsGeneratedIdentifier(tx.EmitContext(), node) && !transformers.IsLocalName(tx.EmitContext(), node) {
location := tx.EmitContext().MostOriginal(node.AsNode())
container := tx.resolver.GetReferencedExportContainer(location, false /*prefixLocals*/)
if container != nil && (ast.IsEnumDeclaration(container) || ast.IsModuleDeclaration(container)) {
containerName := tx.getNamespaceContainerName(container)
memberName := node.Clone(tx.Factory())
tx.EmitContext().SetEmitFlags(memberName, printer.EFNoComments|printer.EFNoSourceMap)
expression := tx.Factory().GetNamespaceMemberName(containerName, memberName, printer.NameOptions{AllowSourceMaps: true})
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
return expression
}
}
return node
}
func (tx *RuntimeSyntaxTransformer) createExportStatementForDeclaration(node *ast.Declaration) *ast.Statement {
exportName := tx.Factory().GetExternalModuleOrNamespaceExportName(tx.getNamespaceContainerName(tx.currentNamespace), node, false /*allowComments*/, true /*allowSourceMaps*/)
localName := tx.Factory().GetLocalName(node)
expression := tx.Factory().NewAssignmentExpression(exportName, localName)
exportAssignmentSourceMapRange := node.Loc
if node.Name() != nil {
exportAssignmentSourceMapRange = exportAssignmentSourceMapRange.WithPos(node.Name().Pos())
}
tx.EmitContext().SetSourceMapRange(expression, exportAssignmentSourceMapRange)
statement := tx.Factory().NewExpressionStatement(expression)
exportStatementSourceMapRange := node.Loc.WithPos(-1)
tx.EmitContext().SetSourceMapRange(statement, exportStatementSourceMapRange)
return statement
}
func (tx *RuntimeSyntaxTransformer) createExportAssignment(name *ast.IdentifierNode, expression *ast.Expression, exportAssignmentSourceMapRange core.TextRange, original *ast.Node) *ast.Expression {
exportName := tx.getNamespaceQualifiedProperty(tx.getNamespaceContainerName(tx.currentNamespace), name)
exportAssignment := tx.Factory().NewAssignmentExpression(exportName, expression)
tx.EmitContext().SetOriginal(exportAssignment, original)
tx.EmitContext().SetSourceMapRange(exportAssignment, exportAssignmentSourceMapRange)
return exportAssignment
}
func (tx *RuntimeSyntaxTransformer) createExportStatement(name *ast.IdentifierNode, expression *ast.Expression, exportAssignmentSourceMapRange core.TextRange, exportStatementSourceMapRange core.TextRange, original *ast.Node) *ast.Statement {
exportStatement := tx.Factory().NewExpressionStatement(tx.createExportAssignment(name, expression, exportAssignmentSourceMapRange, original))
tx.EmitContext().SetOriginal(exportStatement, original)
tx.EmitContext().SetSourceMapRange(exportStatement, exportStatementSourceMapRange)
return exportStatement
}
func (tx *RuntimeSyntaxTransformer) shouldEmitEnumDeclaration(node *ast.EnumDeclaration) bool {
return !ast.IsEnumConst(node.AsNode()) || tx.compilerOptions.ShouldPreserveConstEnums()
}
func (tx *RuntimeSyntaxTransformer) shouldEmitModuleDeclaration(node *ast.ModuleDeclaration) bool {
pn := tx.EmitContext().ParseNode(node.AsNode())
if pn == nil {
// If we can't find a parse tree node, assume the node is instantiated.
return true
}
return ast.IsInstantiatedModule(pn, tx.compilerOptions.ShouldPreserveConstEnums())
}
func getInnermostModuleDeclarationFromDottedModule(moduleDeclaration *ast.ModuleDeclaration) *ast.ModuleDeclaration {
for moduleDeclaration.Body != nil && moduleDeclaration.Body.Kind == ast.KindModuleDeclaration {
moduleDeclaration = moduleDeclaration.Body.AsModuleDeclaration()
}
return moduleDeclaration
}

View File

@@ -0,0 +1,393 @@
package tstransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
type TypeEraserTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
parentNode *ast.Node
currentNode *ast.Node
}
func NewTypeEraserTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opt.CompilerOptions
emitContext := opt.Context
tx := &TypeEraserTransformer{compilerOptions: compilerOptions}
return tx.NewTransformer(tx.visit, emitContext)
}
// Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`.
func (tx *TypeEraserTransformer) pushNode(node *ast.Node) (grandparentNode *ast.Node) {
grandparentNode = tx.parentNode
tx.parentNode = tx.currentNode
tx.currentNode = node
return grandparentNode
}
// Pops the last child node off the ancestor tracking stack, restoring the grandparent node.
func (tx *TypeEraserTransformer) popNode(grandparentNode *ast.Node) {
tx.currentNode = tx.parentNode
tx.parentNode = grandparentNode
}
func (tx *TypeEraserTransformer) elide(node *ast.Statement) *ast.Statement {
return tx.EmitContext().NewNotEmittedStatement(node.AsNode())
}
func (tx *TypeEraserTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsTypeScript == 0 {
return node
}
if ast.IsStatement(node) && ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient) {
return tx.elide(node)
}
grandparentNode := tx.pushNode(node)
defer tx.popNode(grandparentNode)
switch node.Kind {
case
// TypeScript accessibility and readonly modifiers are elided
ast.KindPublicKeyword,
ast.KindPrivateKeyword,
ast.KindProtectedKeyword,
ast.KindAbstractKeyword,
ast.KindOverrideKeyword,
ast.KindConstKeyword,
ast.KindDeclareKeyword,
ast.KindReadonlyKeyword,
// TypeScript type nodes are elided.
ast.KindArrayType,
ast.KindTupleType,
ast.KindOptionalType,
ast.KindRestType,
ast.KindTypeLiteral,
ast.KindTypePredicate,
ast.KindTypeParameter,
ast.KindAnyKeyword,
ast.KindUnknownKeyword,
ast.KindBooleanKeyword,
ast.KindStringKeyword,
ast.KindNumberKeyword,
ast.KindNeverKeyword,
ast.KindVoidKeyword,
ast.KindSymbolKeyword,
ast.KindConstructorType,
ast.KindFunctionType,
ast.KindTypeQuery,
ast.KindTypeReference,
ast.KindUnionType,
ast.KindIntersectionType,
ast.KindConditionalType,
ast.KindParenthesizedType,
ast.KindThisType,
ast.KindTypeOperator,
ast.KindIndexedAccessType,
ast.KindMappedType,
ast.KindLiteralType,
// TypeScript index signatures are elided.
ast.KindIndexSignature:
return nil
case ast.KindInKeyword, ast.KindOutKeyword:
// TypeScript `in`/`out` variance modifiers are elided. These keywords are only
// meaningful as modifiers on type parameters (which are themselves elided), but they may
// appear as a grammar error on other declarations and must not leak into the emitted JS.
// The `in` binary operator shares this token kind, so only elide when used as a modifier.
if tx.parentNode == nil || !ast.IsBinaryExpression(tx.parentNode) {
return nil
}
return tx.Visitor().VisitEachChild(node)
case ast.KindJSImportDeclaration:
// reparsed commonjs are elided
return nil
case ast.KindTypeAliasDeclaration,
ast.KindJSTypeAliasDeclaration,
ast.KindInterfaceDeclaration:
// TypeScript type-only declarations are elided.
return tx.elide(node)
case ast.KindNamespaceExportDeclaration:
// TypeScript namespace export declarations are elided.
return nil
case ast.KindModuleDeclaration:
if !ast.IsIdentifier(node.Name()) ||
!ast.IsInstantiatedModule(node, tx.compilerOptions.ShouldPreserveConstEnums()) ||
getInnermostModuleDeclarationFromDottedModule(node.AsModuleDeclaration()).Body == nil {
// TypeScript module declarations are elided if they are not instantiated or have no body
return tx.elide(node)
}
return tx.Visitor().VisitEachChild(node)
case ast.KindExpressionWithTypeArguments:
n := node.AsExpressionWithTypeArguments()
return tx.Factory().UpdateExpressionWithTypeArguments(n, tx.Visitor().VisitNode(n.Expression), nil)
case ast.KindPropertyDeclaration:
if tx.compilerOptions.ExperimentalDecorators.IsTrue() && ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient|ast.ModifierFlagsAbstract) && ast.HasDecorators(node) {
// declare/abstract props with decorators must be preserved until the decorator transform can process them and remove them
n := node.AsPropertyDeclaration()
return tx.Factory().UpdatePropertyDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
}
if ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient|ast.ModifierFlagsAbstract) {
// TypeScript `declare` fields are elided
return nil
}
n := node.AsPropertyDeclaration()
return tx.Factory().UpdatePropertyDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
case ast.KindConstructor:
n := node.AsConstructorDeclaration()
if ast.NodeIsMissing(n.Body) {
// TypeScript overloads are elided
return nil
}
return tx.Factory().UpdateConstructorDeclaration(n, nil, nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
case ast.KindMethodDeclaration:
n := node.AsMethodDeclaration()
if ast.NodeIsMissing(n.Body) {
// TypeScript overloads are elided
return nil
}
return tx.Factory().UpdateMethodDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), n.AsteriskToken, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
case ast.KindGetAccessor:
n := node.AsGetAccessorDeclaration()
if ast.NodeIsMissing(n.Body) && ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) {
// Abstract accessors are elided
return nil
}
body := tx.Visitor().VisitNode(n.Body)
if body == nil {
body = tx.Factory().NewBlock(tx.Factory().NewNodeList(nil), false)
}
return tx.Factory().UpdateGetAccessorDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, body)
case ast.KindSetAccessor:
n := node.AsSetAccessorDeclaration()
if ast.NodeIsMissing(n.Body) && ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) {
// Abstract accessors are elided
return nil
}
body := tx.Visitor().VisitNode(n.Body)
if body == nil {
body = tx.Factory().NewBlock(tx.Factory().NewNodeList(nil), false)
}
return tx.Factory().UpdateSetAccessorDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, body)
case ast.KindVariableDeclaration:
n := node.AsVariableDeclaration()
updated := tx.Factory().UpdateVariableDeclaration(n, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
if n.Type != nil {
tx.EmitContext().SetTypeNode(updated.AsVariableDeclaration().Name(), n.Type)
}
return updated
case ast.KindHeritageClause:
n := node.AsHeritageClause()
if n.Token == ast.KindImplementsKeyword {
// TypeScript `implements` clauses are elided
return nil
}
return tx.Factory().UpdateHeritageClause(n, n.Token, tx.Visitor().VisitNodes(n.Types))
case ast.KindClassDeclaration:
n := node.AsClassDeclaration()
return tx.Factory().UpdateClassDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.HeritageClauses), tx.Visitor().VisitNodes(n.Members))
case ast.KindClassExpression:
n := node.AsClassExpression()
return tx.Factory().UpdateClassExpression(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.HeritageClauses), tx.Visitor().VisitNodes(n.Members))
case ast.KindFunctionDeclaration:
n := node.AsFunctionDeclaration()
if ast.NodeIsMissing(n.Body) {
// TypeScript overloads are elided
return tx.elide(node)
}
return tx.Factory().UpdateFunctionDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), n.AsteriskToken, tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
case ast.KindFunctionExpression:
n := node.AsFunctionExpression()
return tx.Factory().UpdateFunctionExpression(n, tx.Visitor().VisitModifiers(n.Modifiers()), n.AsteriskToken, tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
case ast.KindArrowFunction:
n := node.AsArrowFunction()
return tx.Factory().UpdateArrowFunction(n, tx.Visitor().VisitModifiers(n.Modifiers()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, n.EqualsGreaterThanToken, tx.Visitor().VisitNode(n.Body))
case ast.KindParameter:
if ast.IsThisParameter(node) {
// TypeScript `this` parameters are elided
return nil
}
n := node.AsParameterDeclaration()
// preserve parameter property modifiers to be handled by the runtime transformer
var modifiers *ast.ModifierList
if ast.IsParameterPropertyDeclaration(node, tx.parentNode) {
modifiers = transformers.ExtractModifiers(tx.EmitContext(), n.Modifiers(), ast.ModifierFlagsParameterPropertyModifier)
}
// preserve decorators for the decorator transforms
if ast.HasDecorators(node) {
decorators := node.Decorators()
visited, _ := tx.Visitor().VisitSlice(decorators)
if modifiers == nil {
modifiers = tx.Factory().NewModifierList(visited)
} else {
modifiers = tx.Factory().NewModifierList(slices.Concat(modifiers.Nodes, visited))
}
}
return tx.Factory().UpdateParameterDeclaration(n, modifiers, n.DotDotDotToken, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
case ast.KindCallExpression:
n := node.AsCallExpression()
return tx.Factory().UpdateCallExpression(n, tx.Visitor().VisitNode(n.Expression), n.QuestionDotToken, nil, tx.Visitor().VisitNodes(n.Arguments), n.Flags)
case ast.KindNewExpression:
n := node.AsNewExpression()
return tx.Factory().UpdateNewExpression(n, tx.Visitor().VisitNode(n.Expression), nil, tx.Visitor().VisitNodes(n.Arguments))
case ast.KindTaggedTemplateExpression:
n := node.AsTaggedTemplateExpression()
return tx.Factory().UpdateTaggedTemplateExpression(n, tx.Visitor().VisitNode(n.Tag), n.QuestionDotToken, nil, tx.Visitor().VisitNode(n.Template), n.Flags)
case ast.KindNonNullExpression, ast.KindTypeAssertionExpression, ast.KindAsExpression, ast.KindSatisfiesExpression:
partial := tx.Factory().NewPartiallyEmittedExpression(tx.Visitor().VisitNode(node.Expression()))
tx.EmitContext().SetOriginal(partial, node)
partial.Loc = node.Loc
return partial
case ast.KindParenthesizedExpression:
if !ast.IsJSDocTypeAssertion(node) {
n := node.AsParenthesizedExpression()
expression := ast.SkipOuterExpressions(n.Expression, ast.OEKAllExceptAssertionsOrExpressionsWithTypeArguments)
if ast.IsAssertionExpression(expression) || ast.IsSatisfiesExpression(expression) {
partial := tx.Factory().NewPartiallyEmittedExpression(tx.Visitor().VisitNode(n.Expression))
tx.EmitContext().SetOriginal(partial, node)
partial.Loc = node.Loc
return partial
}
}
return tx.Visitor().VisitEachChild(node)
case ast.KindJsxSelfClosingElement:
n := node.AsJsxSelfClosingElement()
return tx.Factory().UpdateJsxSelfClosingElement(n, tx.Visitor().VisitNode(n.TagName), nil, tx.Visitor().VisitNode(n.Attributes))
case ast.KindJsxOpeningElement:
n := node.AsJsxOpeningElement()
return tx.Factory().UpdateJsxOpeningElement(n, tx.Visitor().VisitNode(n.TagName), nil, tx.Visitor().VisitNode(n.Attributes))
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
if n.IsTypeOnly {
// elide type-only imports
return nil
}
return tx.Visitor().VisitEachChild(node)
case ast.KindImportDeclaration:
n := node.AsImportDeclaration()
if n.ImportClause == nil {
// Do not elide a side-effect only import declaration.
// import "foo";
return node
}
importClause := tx.Visitor().VisitNode(n.ImportClause)
if importClause == nil {
return nil
}
return tx.Factory().UpdateImportDeclaration(n, n.Modifiers(), importClause, n.ModuleSpecifier, n.Attributes)
case ast.KindImportClause:
n := node.AsImportClause()
if n.IsTypeOnly() {
// Always elide type-only imports
return nil
}
name := n.Name()
namedBindings := tx.Visitor().VisitNode(n.NamedBindings)
if name == nil && namedBindings == nil {
// all import bindings were elided
return nil
}
return tx.Factory().UpdateImportClause(n, n.PhaseModifier, name, namedBindings)
case ast.KindNamedImports:
n := node.AsNamedImports()
if len(n.Elements.Nodes) == 0 {
// Do not elide a side-effect only import declaration.
return node
}
elements := tx.Visitor().VisitNodes(n.Elements)
if !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && len(elements.Nodes) == 0 {
// all import specifiers were elided
return nil
}
return tx.Factory().UpdateNamedImports(n, elements)
case ast.KindImportSpecifier:
n := node.AsImportSpecifier()
if n.IsTypeOnly {
// elide type-only or unused imports
return nil
}
return node
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
if n.IsTypeOnly {
// elide type-only exports
return nil
}
var exportClause *ast.Node
if n.ExportClause != nil {
exportClause = tx.Visitor().VisitNode(n.ExportClause)
if exportClause == nil {
// all export bindings were elided
return nil
}
}
return tx.Factory().UpdateExportDeclaration(n, nil /*modifiers*/, false /*isTypeOnly*/, exportClause, tx.Visitor().VisitNode(n.ModuleSpecifier), tx.Visitor().VisitNode(n.Attributes))
case ast.KindNamedExports:
n := node.AsNamedExports()
if len(n.Elements.Nodes) == 0 {
// Do not elide an empty export declaration.
return node
}
elements := tx.Visitor().VisitNodes(n.Elements)
if !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && len(elements.Nodes) == 0 {
// all export specifiers were elided
return nil
}
return tx.Factory().UpdateNamedExports(n, elements)
case ast.KindExportSpecifier:
n := node.AsExportSpecifier()
if n.IsTypeOnly {
// elide unused export
return nil
}
return node
case ast.KindEnumDeclaration:
if ast.IsEnumConst(node) {
return node
}
return tx.Visitor().VisitEachChild(node)
default:
return tx.Visitor().VisitEachChild(node)
}
}

View File

@@ -0,0 +1,106 @@
package tstransforms_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/testutil/emittestutil"
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
"github.com/microsoft/typescript-go/internal/transformers"
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
)
func TestTypeEraser(t *testing.T) {
t.Parallel()
data := []struct {
title string
input string
output string
jsx bool
vms bool
}{
{title: "Modifiers", input: "class C { public x; private y }", output: "class C {\n x;\n y;\n}"},
{title: "InterfaceDeclaration", input: "interface I { }", output: ""},
{title: "TypeAliasDeclaration", input: "type T = U;", output: ""},
{title: "NamespaceExportDeclaration", input: "export as namespace N;", output: ""},
{title: "UninstantiatedNamespace1", input: "namespace N {}", output: ""},
{title: "UninstantiatedNamespace2", input: "namespace N { export interface I {} }", output: ""},
{title: "UninstantiatedNamespace3", input: "namespace N { export type T = U; }", output: ""},
{title: "ExpressionWithTypeArguments", input: "F<T>", output: "F;"},
{title: "PropertyDeclaration1", input: "class C { declare x; }", output: "class C {\n}"},
{title: "PropertyDeclaration2", input: "class C { public x: number; }", output: "class C {\n x;\n}"},
{title: "PropertyDeclaration3", input: "class C { public static x: number; }", output: "class C {\n static x;\n}"},
{title: "ConstructorDeclaration1", input: "class C { constructor(); }", output: "class C {\n}"},
{title: "ConstructorDeclaration2", input: "class C { public constructor() {} }", output: "class C {\n constructor() { }\n}"},
{title: "MethodDeclaration1", input: "class C { m(); }", output: "class C {\n}"},
{title: "MethodDeclaration2", input: "class C { public m<T>(): U {} }", output: "class C {\n m() { }\n}"},
{title: "MethodDeclaration3", input: "class C { public static m<T>(): U {} }", output: "class C {\n static m() { }\n}"},
{title: "GetAccessorDeclaration1", input: "class C { get m(); }", output: "class C {\n get m() { }\n}"},
{title: "GetAccessorDeclaration2", input: "class C { public get m<T>(): U {} }", output: "class C {\n get m() { }\n}"},
{title: "GetAccessorDeclaration3", input: "class C { public static get m<T>(): U {} }", output: "class C {\n static get m() { }\n}"},
{title: "SetAccessorDeclaration1", input: "class C { set m(v); }", output: "class C {\n set m(v) { }\n}"},
{title: "SetAccessorDeclaration2", input: "class C { public set m<T>(v): U {} }", output: "class C {\n set m(v) { }\n}"},
{title: "SetAccessorDeclaration3", input: "class C { public static set m<T>(v): U {} }", output: "class C {\n static set m(v) { }\n}"},
{title: "IndexSignature", input: "class C { [key: string]: number; }", output: "class C {\n}"},
{title: "VariableDeclaration1", input: "declare var a;", output: ""},
{title: "VariableDeclaration2", input: "var a: number", output: "var a;"},
{title: "HeritageClause", input: "class C implements I {}", output: "class C {\n}"},
{title: "ClassDeclaration1", input: "declare class C {}", output: ""},
{title: "ClassDeclaration2", input: "class C<T> {}", output: "class C {\n}"},
{title: "ClassExpression", input: "(class C<T> {})", output: "(class C {\n});"},
{title: "FunctionDeclaration1", input: "declare function f() {}", output: ""},
{title: "FunctionDeclaration2", input: "function f();", output: ""},
{title: "FunctionDeclaration3", input: "function f<T>(): U {}", output: "function f() { }"},
{title: "FunctionExpression", input: "(function f<T>(): U {})", output: "(function f() { });"},
{title: "ArrowFunction", input: "(<T>(): U => {})", output: "(() => { });"},
{title: "ParameterDeclaration", input: "function f(this: x, a: number, b?: boolean) {}", output: "function f(a, b) { }"},
{title: "CallExpression", input: "f<T>()", output: "f();"},
{title: "NewExpression1", input: "new f<T>()", output: "new f();"},
{title: "NewExpression2", input: "new f<T>", output: "new f;"},
{title: "TaggedTemplateExpression", input: "f<T>``", output: "f ``;"},
{title: "NonNullExpression", input: "x!", output: "x;"},
{title: "TypeAssertionExpression#1", input: "<T>x", output: "x;"},
{title: "TypeAssertionExpression#2", input: "(<T>x).c", output: "x.c;"},
{title: "AsExpression#1", input: "x as T", output: "x;"},
{title: "AsExpression#2", input: "(x as T).c", output: "x.c;"},
{title: "SatisfiesExpression#1", input: "x satisfies T", output: "x;"},
{title: "SatisfiesExpression#2", input: "(x satisfies T).c", output: "x.c;"},
{title: "JsxSelfClosingElement", input: "<x<T> />", output: "<x />;", jsx: true},
{title: "JsxOpeningElement", input: "<x<T>></x>", output: "<x></x>;", jsx: true},
{title: "ImportEqualsDeclaration#1", input: "import x = require(\"m\");", output: "import x = require(\"m\");"},
{title: "ImportEqualsDeclaration#2", input: "import type x = require(\"m\");", output: ""},
{title: "ImportEqualsDeclaration#3", input: "import x = y;", output: "import x = y;"},
{title: "ImportEqualsDeclaration#4", input: "import type x = y;", output: ""},
{title: "ImportDeclaration#1", input: "import \"m\";", output: "import \"m\";"},
{title: "ImportDeclaration#2", input: "import * as x from \"m\"; x;", output: "import * as x from \"m\";\nx;"},
{title: "ImportDeclaration#3", input: "import x from \"m\"; x;", output: "import x from \"m\";\nx;"},
{title: "ImportDeclaration#4", input: "import { x } from \"m\"; x;", output: "import { x } from \"m\";\nx;"},
{title: "ImportDeclaration#5", input: "import type * as x from \"m\";", output: ""},
{title: "ImportDeclaration#6", input: "import type x from \"m\";", output: ""},
{title: "ImportDeclaration#7", input: "import type { x } from \"m\";", output: ""},
{title: "ImportDeclaration#8", input: "import { type x } from \"m\";", output: ""},
{title: "ImportDeclaration#9", input: "import { type x } from \"m\";", output: "import {} from \"m\";", vms: true},
{title: "ExportDeclaration#1", input: "export * from \"m\";", output: "export * from \"m\";"},
{title: "ExportDeclaration#2", input: "export * as x from \"m\";", output: "export * as x from \"m\";"},
{title: "ExportDeclaration#3", input: "export { x } from \"m\";", output: "export { x } from \"m\";"},
{title: "ExportDeclaration#4", input: "export type * from \"m\";", output: ""},
{title: "ExportDeclaration#5", input: "export type * as x from \"m\";", output: ""},
{title: "ExportDeclaration#6", input: "export type { x } from \"m\";", output: ""},
{title: "ExportDeclaration#7", input: "export { type x } from \"m\";", output: ""},
{title: "ExportDeclaration#7", input: "export { type x } from \"m\";", output: "export {} from \"m\";", vms: true},
}
for _, rec := range data {
t.Run(rec.title, func(t *testing.T) {
t.Parallel()
file := parsetestutil.ParseTypeScript(rec.input, rec.jsx)
parsetestutil.CheckDiagnostics(t, file)
compilerOptions := &core.CompilerOptions{}
if rec.vms {
compilerOptions.VerbatimModuleSyntax = core.TSTrue
}
emittestutil.CheckEmit(t, nil, tstransforms.NewTypeEraserTransformer(&transformers.TransformOptions{CompilerOptions: compilerOptions, Context: printer.NewEmitContext()}).TransformSourceFile(file), rec.output)
})
}
}

View File

@@ -0,0 +1,511 @@
package tstransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type metadataSerializer struct {
resolver printer.EmitResolver
languageVersion core.ScriptTarget
strictNullChecks bool
f *printer.NodeFactory
ec *printer.EmitContext
c metadataSerializerContext
}
type metadataSerializerContext struct {
currentLexicalScope *ast.Node
currentNameScope *ast.Node
serializingConditionalTypeBranch bool
}
func newMetadataSerializer(resolver printer.EmitResolver, f *printer.NodeFactory, ec *printer.EmitContext, languageVersion core.ScriptTarget, strictNullChecks bool) *metadataSerializer {
return &metadataSerializer{resolver: resolver, languageVersion: languageVersion, f: f, ec: ec, strictNullChecks: strictNullChecks}
}
func (s *metadataSerializer) setContext(ctx metadataSerializerContext) {
s.c = ctx
}
func (s *metadataSerializer) SerializeTypeOfNode(ctx metadataSerializerContext, node *ast.Node, container *ast.Node) *ast.Node {
oldCtx := s.c
s.c = ctx
defer s.setContext(oldCtx)
return s.serializeTypeOfNode(node, container)
}
func (s *metadataSerializer) SerializeParameterTypesOfNode(ctx metadataSerializerContext, node *ast.Node, container *ast.Node) *ast.Node {
oldCtx := s.c
s.c = ctx
defer s.setContext(oldCtx)
return s.serializeParameterTypesOfNode(node, container)
}
func (s *metadataSerializer) SerializeReturnTypeOfNode(ctx metadataSerializerContext, node *ast.Node) *ast.Node {
oldCtx := s.c
s.c = ctx
defer s.setContext(oldCtx)
return s.serializeReturnTypeOfNode(node)
}
func GetSetAccessorValueParameter(node *ast.SetAccessorDeclaration) *ast.Node {
if node != nil && len(node.Parameters.Nodes) > 0 {
if len(node.Parameters.Nodes) >= 2 && ast.IsThisParameter(node.Parameters.Nodes[0]) {
return node.Parameters.Nodes[1]
}
return node.Parameters.Nodes[0]
}
return nil
}
/**
* Get the type annotation for the value parameter.
*
* @internal
*/
func getSetAccessorTypeAnnotationNode(node *ast.SetAccessorDeclaration) *ast.Node {
p := GetSetAccessorValueParameter(node)
if p != nil && p.Type() != nil {
return p.Type()
}
return nil
}
func getAccessorTypeNode(node *ast.Node, container *ast.Node) *ast.Node {
accessors := ast.GetAllAccessorDeclarations(container.Members(), node)
if accessors.SetAccessor != nil {
return getSetAccessorTypeAnnotationNode(accessors.SetAccessor)
}
if accessors.GetAccessor != nil {
return accessors.GetAccessor.Type
}
return nil
}
/**
* Serializes the type of a node for use with decorator type metadata.
* @param node The node that should have its type serialized.
*/
func (s *metadataSerializer) serializeTypeOfNode(node *ast.Node, container *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindPropertyDeclaration, ast.KindParameter:
return s.serializeTypeNode(node.Type())
case ast.KindGetAccessor, ast.KindSetAccessor:
return s.serializeTypeNode(getAccessorTypeNode(node, container))
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindMethodDeclaration:
return s.f.NewIdentifier("Function")
default:
return s.f.NewVoidZeroExpression()
}
}
/**
* Serializes the type of a node for use with decorator type metadata.
* @param node The node that should have its type serialized.
*/
func (s *metadataSerializer) serializeParameterTypesOfNode(node *ast.Node, container *ast.Node) *ast.Node {
var valueDeclaration *ast.Node
if ast.IsClassLike(node) {
valueDeclaration = ast.GetFirstConstructorWithBody(node)
} else if ast.IsFunctionLike(node) && ast.NodeIsPresent(node.Body()) {
valueDeclaration = node
}
if valueDeclaration == nil {
return s.f.NewArrayLiteralExpression(s.f.NewNodeList([]*ast.Node{}), false)
}
var expressions []*ast.Node
parameters := getParametersOfDecoratedDeclaration(valueDeclaration, container)
for i, parameter := range parameters.Nodes {
if i == 0 && ast.IsIdentifier(parameter.Name()) && parameter.Name().Text() == "this" {
continue
}
if parameter.AsParameterDeclaration().DotDotDotToken != nil {
expressions = append(expressions, s.serializeTypeNode(ast.GetRestParameterElementType(parameter.Type())))
} else {
expressions = append(expressions, s.serializeTypeOfNode(parameter, container))
}
}
return s.f.NewArrayLiteralExpression(s.f.NewNodeList(expressions), false)
}
func getParametersOfDecoratedDeclaration(node *ast.Node, container *ast.Node) *ast.NodeList {
if container != nil && node.Kind == ast.KindGetAccessor {
acc := ast.GetAllAccessorDeclarations(container.Members(), node)
if acc.SetAccessor != nil {
return acc.SetAccessor.Parameters
}
}
return node.ParameterList()
}
/**
* Serializes the return type of a node for use with decorator type metadata.
* @param node The node that should have its return type serialized.
*/
func (s *metadataSerializer) serializeReturnTypeOfNode(node *ast.Node) *ast.Node {
if ast.IsFunctionLike(node) && node.Type() != nil {
return s.serializeTypeNode(node.Type())
} else if ast.IsAsyncFunction(node) {
return s.f.NewIdentifier("Promise")
}
return s.f.NewVoidZeroExpression()
}
/**
* Serializes a type node for use with decorator type metadata.
*
* Types are serialized in the following fashion:
* - Void types point to "undefined" (e.g. "void 0")
* - Function and Constructor types point to the global "Function" constructor.
* - Interface types with a call or construct signature types point to the global
* "Function" constructor.
* - Array and Tuple types point to the global "Array" constructor.
* - Type predicates and booleans point to the global "Boolean" constructor.
* - String literal types and strings point to the global "String" constructor.
* - Enum and number types point to the global "Number" constructor.
* - Symbol types point to the global "Symbol" constructor.
* - Type references to classes (or class-like variables) point to the constructor for the class.
* - Anything else points to the global "Object" constructor.
*
* @param node The type node to serialize.
*/
func (s *metadataSerializer) serializeTypeNode(node *ast.Node) *ast.Node {
if node == nil {
return s.f.NewIdentifier("Object")
}
node = ast.SkipTypeParentheses(node)
switch node.Kind {
case ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNeverKeyword:
return s.f.NewVoidZeroExpression()
case ast.KindFunctionType, ast.KindConstructorType:
return s.f.NewIdentifier("Function")
case ast.KindArrayType, ast.KindTupleType:
return s.f.NewIdentifier("Array")
case ast.KindTypePredicate:
if node.AsTypePredicateNode().AssertsModifier != nil {
return s.f.NewVoidZeroExpression()
}
return s.f.NewIdentifier("Boolean")
case ast.KindBooleanKeyword:
return s.f.NewIdentifier("Boolean")
case ast.KindTemplateLiteralType, ast.KindStringKeyword:
return s.f.NewIdentifier("String")
case ast.KindObjectKeyword:
return s.f.NewIdentifier("Object")
case ast.KindLiteralType:
return s.serializeLiteralOfLiteralTypeNode(node.AsLiteralTypeNode().Literal)
case ast.KindNumberKeyword:
return s.f.NewIdentifier("Number")
case ast.KindBigIntKeyword:
return s.serializeBigIntConstructor()
case ast.KindSymbolKeyword:
return s.f.NewIdentifier("Symbol")
case ast.KindTypeReference:
return s.serializeTypeReferenceNode(node.AsTypeReferenceNode())
case ast.KindIntersectionType:
return s.serializeUnionOrIntersectionConstituents(node.AsIntersectionTypeNode().Types.Nodes, true)
case ast.KindUnionType:
return s.serializeUnionOrIntersectionConstituents(node.AsUnionTypeNode().Types.Nodes, false)
case ast.KindConditionalType:
oldState := s.c.serializingConditionalTypeBranch
s.c.serializingConditionalTypeBranch = true
defer func() { s.c.serializingConditionalTypeBranch = oldState }()
return s.serializeUnionOrIntersectionConstituents([]*ast.Node{node.AsConditionalTypeNode().TrueType, node.AsConditionalTypeNode().FalseType}, false)
case ast.KindTypeOperator:
if node.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword {
return s.serializeTypeNode(node.Type())
}
// TODO: why is `unique symbol` not handled as `Symbol`? This falls back to `Object`
case ast.KindTypeQuery, ast.KindIndexedAccessType, ast.KindMappedType, ast.KindTypeLiteral, ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindThisType, ast.KindImportType:
break
// handle JSDoc types from an invalid parse
case ast.KindJSDocAllType, ast.KindJSDocVariadicType:
break
case ast.KindJSDocNullableType, ast.KindJSDocNonNullableType, ast.KindJSDocOptionalType:
return s.serializeTypeNode(node.Type())
default:
debug.FailBadSyntaxKind(node)
return nil
}
return s.f.NewIdentifier("Object")
}
func (s *metadataSerializer) serializeUnionOrIntersectionConstituents(types []*ast.Node, isIntersection bool) *ast.Node {
// Note when updating logic here also update `getEntityNameForDecoratorMetadata` in checker.ts so that aliases can be marked as referenced
var serializedType *ast.Node
for _, typeNode := range types {
typeNode = ast.SkipTypeParentheses(typeNode)
if typeNode.Kind == ast.KindNeverKeyword {
if isIntersection {
return s.f.NewVoidZeroExpression() // Reduce to `never` in an intersection
}
continue // Elide `never` in a union
}
if typeNode.Kind == ast.KindUnknownKeyword {
if !isIntersection {
return s.f.NewIdentifier("Object") // Reduce to `unknown` in a union
}
continue // Elide `unknown` in an intersection
}
if typeNode.Kind == ast.KindAnyKeyword {
return s.f.NewIdentifier("Object") // Reduce to `any` in a union or intersection
}
if !s.strictNullChecks && ((ast.IsLiteralTypeNode(typeNode) && typeNode.AsLiteralTypeNode().Literal.Kind == ast.KindNullKeyword) || typeNode.Kind == ast.KindUndefinedKeyword) {
continue // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
}
serializedConstituent := s.serializeTypeNode(typeNode)
if ast.IsIdentifier(serializedConstituent) && serializedConstituent.AsIdentifier().Text == "Object" {
// One of the individual is global object, return immediately
return serializedConstituent
}
// If there exists union that is not `void 0` expression, check if the the common type is identifier.
// anything more complex and we will just default to Object
if serializedType != nil {
// Different types
if !s.equateSerializedTypeNodes(serializedType, serializedConstituent) {
return s.f.NewIdentifier("Object")
}
} else {
// Initialize the union type
serializedType = serializedConstituent
}
}
// If we were able to find common type, use it
if serializedType != nil {
return serializedType
}
return s.f.NewVoidZeroExpression() // Fallback is only hit if all union constituents are null/undefined/never
}
func (s *metadataSerializer) serializeLiteralOfLiteralTypeNode(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral:
return s.f.NewIdentifier("String")
case ast.KindPrefixUnaryExpression:
operand := node.AsPrefixUnaryExpression().Operand
switch operand.Kind {
case ast.KindNumericLiteral, ast.KindBigIntLiteral:
return s.serializeLiteralOfLiteralTypeNode(operand)
default:
debug.FailBadSyntaxKind(operand)
}
case ast.KindNumericLiteral:
return s.f.NewIdentifier("Number")
case ast.KindBigIntLiteral:
return s.serializeBigIntConstructor()
case ast.KindTrueKeyword, ast.KindFalseKeyword:
return s.f.NewIdentifier("Boolean")
case ast.KindNullKeyword:
return s.f.NewVoidZeroExpression()
default:
debug.FailBadSyntaxKind(node)
return nil
}
return nil
}
/**
* Serializes a TypeReferenceNode to an appropriate JS constructor value for use with decorator type metadata.
* @param node The type reference node.
*/
func (s *metadataSerializer) serializeTypeReferenceNode(node *ast.TypeReferenceNode) *ast.Node {
serialScope := s.c.currentNameScope
if serialScope == nil {
serialScope = s.c.currentLexicalScope
}
kind := s.resolver.GetTypeReferenceSerializationKind(s.ec.ParseNode(node.TypeName), s.ec.ParseNode(serialScope))
switch kind {
case printer.TypeReferenceSerializationKindUnknown:
// From conditional type type reference that cannot be resolved is Similar to any or unknown
if s.c.serializingConditionalTypeBranch {
return s.f.NewIdentifier("Object")
}
serialized := s.serializeEntityNameAsExpressionFallback(node.TypeName)
temp := s.f.NewTempVariable()
s.ec.AddVariableDeclaration(temp)
return s.f.NewConditionalExpression(
s.f.NewTypeCheck(s.f.NewAssignmentExpression(temp, serialized), "function"),
s.f.NewToken(ast.KindQuestionToken),
temp,
s.f.NewToken(ast.KindColonToken),
s.f.NewIdentifier("Object"),
)
case printer.TypeReferenceSerializationKindTypeWithConstructSignatureAndValue:
return s.serializeEntityNameAsExpression(node.TypeName)
case printer.TypeReferenceSerializationKindVoidNullableOrNeverType:
return s.f.NewVoidZeroExpression()
case printer.TypeReferenceSerializationKindBigIntLikeType:
return s.serializeBigIntConstructor()
case printer.TypeReferenceSerializationKindBooleanType:
return s.f.NewIdentifier("Boolean")
case printer.TypeReferenceSerializationKindNumberLikeType:
return s.f.NewIdentifier("Number")
case printer.TypeReferenceSerializationKindStringLikeType:
return s.f.NewIdentifier("String")
case printer.TypeReferenceSerializationKindArrayLikeType:
return s.f.NewIdentifier("Array")
case printer.TypeReferenceSerializationKindESSymbolType:
return s.f.NewIdentifier("Symbol")
case printer.TypeReferenceSerializationKindTypeWithCallSignature:
return s.f.NewIdentifier("Function")
case printer.TypeReferenceSerializationKindPromise:
return s.f.NewIdentifier("Promise")
case printer.TypeReferenceSerializationKindObjectType:
return s.f.NewIdentifier("Object")
default:
debug.AssertNever(kind, "unknown type reference serialization kind")
return nil
}
}
func (s *metadataSerializer) serializeBigIntConstructor() *ast.Node {
if s.languageVersion >= core.ScriptTargetES2020 {
return s.f.NewIdentifier("BigInt")
}
return s.f.NewConditionalExpression(
s.f.NewTypeCheck(s.f.NewIdentifier("BigInt"), "function"),
s.f.NewToken(ast.KindQuestionToken),
s.f.NewIdentifier("BigInt"),
s.f.NewToken(ast.KindColonToken),
s.f.NewIdentifier("Object"),
)
}
/**
* Serializes an entity name as an expression for decorator type metadata.
* @param node The entity name to serialize.
*/
func (s *metadataSerializer) serializeEntityNameAsExpression(node *ast.EntityName) *ast.Node {
switch node.Kind {
case ast.KindIdentifier:
// Create a clone of the name with a new parent, and treat it as if it were
// a source tree node for the purposes of the checker.
name := node.Clone(s.f)
name.Loc = node.Loc
s.ec.UnsetOriginal(name) // make this identifier emulate a parse node, making it behave correctly when inspected by the module transforms
name.Parent = s.ec.ParseNode(s.c.currentLexicalScope) //nolint:customlint // ensure the parent is set to a parse tree node.
return name
case ast.KindQualifiedName:
return s.serializeQualifiedNameAsExpression(node.AsQualifiedName())
}
return nil
}
/**
* Serializes an qualified name as an expression for decorator type metadata.
* @param node The qualified name to serialize.
*/
func (s *metadataSerializer) serializeQualifiedNameAsExpression(node *ast.QualifiedName) *ast.Node {
return s.f.NewPropertyAccessExpression(s.serializeEntityNameAsExpression(node.Left), nil, node.Right, ast.NodeFlagsNone)
}
/**
* Serializes an entity name which may not exist at runtime, but whose access shouldn't throw
* @param node The entity name to serialize.
*/
func (s *metadataSerializer) serializeEntityNameAsExpressionFallback(node *ast.EntityName) *ast.Node {
if node.Kind == ast.KindIdentifier {
// A -> typeof A !== "undefined" && A
copied := s.serializeEntityNameAsExpression(node)
return s.createCheckedValue(copied, copied)
}
if node.AsQualifiedName().Left.Kind == ast.KindIdentifier {
// A.B -> typeof A !== "undefined" && A.B
return s.createCheckedValue(s.serializeEntityNameAsExpression(node.AsQualifiedName().Left), s.serializeEntityNameAsExpression(node))
}
// A.B.C -> typeof A !== "undefined" && (_a = A.B) !== void 0 && _a.C
left := s.serializeEntityNameAsExpressionFallback(node.AsQualifiedName().Left)
temp := s.f.NewTempVariable()
s.ec.AddVariableDeclaration(temp)
return s.f.NewLogicalANDExpression(
s.f.NewLogicalANDExpression(
left.AsBinaryExpression().Left,
s.f.NewStrictInequalityExpression(s.f.NewAssignmentExpression(temp, left.AsBinaryExpression().Right), s.f.NewVoidZeroExpression()),
),
s.f.NewPropertyAccessExpression(temp, nil, node.AsQualifiedName().Right, ast.NodeFlagsNone),
)
}
/**
* Produces an expression that results in `right` if `left` is not undefined at runtime:
*
* ```
* typeof left !== "undefined" && right
* ```
*
* We use `typeof L !== "undefined"` (rather than `L !== undefined`) since `L` may not be declared.
* It's acceptable for this expression to result in `false` at runtime, as the result is intended to be
* further checked by any containing expression.
*/
func (s *metadataSerializer) createCheckedValue(left *ast.Node, right *ast.Node) *ast.Node {
return s.f.NewLogicalANDExpression(
s.f.NewStrictInequalityExpression(s.f.NewTypeOfExpression(left), s.f.NewStringLiteral("undefined", ast.TokenFlagsNone)),
right,
)
}
func (s *metadataSerializer) equateSerializedTypeNodes(left *ast.Node, right *ast.Node) bool {
// temp vars used in fallback
if transformers.IsGeneratedIdentifier(s.ec, left) {
return transformers.IsGeneratedIdentifier(s.ec, right)
}
// entity names
if ast.IsIdentifier(left) {
return ast.IsIdentifier(right) && left.Text() == right.Text()
}
if ast.IsPropertyAccessExpression(left) {
return ast.IsPropertyAccessExpression(right) && s.equateSerializedTypeNodes(left.Expression(), right.Expression()) && s.equateSerializedTypeNodes(left.Name(), right.Name())
}
// `void 0`
if ast.IsVoidExpression(left) {
return ast.IsVoidExpression(right) && ast.IsNumericLiteral(left.Expression()) && ast.IsNumericLiteral(right.Expression()) && left.Expression().Text() == "0" && right.Expression().Text() == "0"
}
// `"undefined"` or `"function"` in `typeof` checks
if ast.IsStringLiteral(left) {
return ast.IsStringLiteral(right) && left.Text() == right.Text()
}
// used in `typeof` checks for fallback
if ast.IsTypeOfExpression(left) {
return ast.IsTypeOfExpression(right) && s.equateSerializedTypeNodes(left.Expression(), right.Expression())
}
// parens in `typeof` checks with temps
if ast.IsParenthesizedExpression(left) {
return ast.IsParenthesizedExpression(right) && s.equateSerializedTypeNodes(left.Expression(), right.Expression())
}
// conditionals used in fallback
if ast.IsConditionalExpression(left) {
return ast.IsConditionalExpression(right) && s.equateSerializedTypeNodes(left.AsConditionalExpression().Condition, right.AsConditionalExpression().Condition) && s.equateSerializedTypeNodes(left.AsConditionalExpression().WhenTrue, right.AsConditionalExpression().WhenTrue) && s.equateSerializedTypeNodes(left.AsConditionalExpression().WhenFalse, right.AsConditionalExpression().WhenFalse)
}
// logical binary and assignments used in fallback
if ast.IsBinaryExpression(left) {
return ast.IsBinaryExpression(right) && left.AsBinaryExpression().OperatorToken.Kind == right.AsBinaryExpression().OperatorToken.Kind && s.equateSerializedTypeNodes(left.AsBinaryExpression().Left, right.AsBinaryExpression().Left) && s.equateSerializedTypeNodes(left.AsBinaryExpression().Right, right.AsBinaryExpression().Right)
}
return false
}

View File

@@ -0,0 +1,29 @@
package tstransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/jsnum"
"github.com/microsoft/typescript-go/internal/printer"
)
func constantExpression(value any, factory *printer.NodeFactory) *ast.Expression {
switch value := value.(type) {
case string:
return factory.NewStringLiteral(value, ast.TokenFlagsNone)
case jsnum.Number:
if value.IsInf() {
if value > 0 {
return factory.NewIdentifier("Infinity")
}
return factory.NewPrefixUnaryExpression(ast.KindMinusToken, factory.NewIdentifier("Infinity"))
}
if value.IsNaN() {
return factory.NewIdentifier("NaN")
}
if value < 0 {
return factory.NewPrefixUnaryExpression(ast.KindMinusToken, constantExpression(-value, factory))
}
return factory.NewNumericLiteral(value.String(), ast.TokenFlagsNone)
}
return nil
}

View File

@@ -0,0 +1,375 @@
package transformers
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
)
func IsGeneratedIdentifier(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
return emitContext.HasAutoGenerateInfo(name)
}
func IsHelperName(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
return emitContext.EmitFlags(name)&printer.EFHelperName != 0
}
func IsLocalName(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
return emitContext.EmitFlags(name)&printer.EFLocalName != 0
}
func IsExportName(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
return emitContext.EmitFlags(name)&printer.EFExportName != 0
}
func IsIdentifierReference(name *ast.IdentifierNode, parent *ast.Node) bool {
switch parent.Kind {
case ast.KindBinaryExpression,
ast.KindPrefixUnaryExpression,
ast.KindPostfixUnaryExpression,
ast.KindYieldExpression,
ast.KindAsExpression,
ast.KindSatisfiesExpression,
ast.KindElementAccessExpression,
ast.KindNonNullExpression,
ast.KindSpreadElement,
ast.KindSpreadAssignment,
ast.KindParenthesizedExpression,
ast.KindArrayLiteralExpression,
ast.KindDeleteExpression,
ast.KindTypeOfExpression,
ast.KindVoidExpression,
ast.KindAwaitExpression,
ast.KindTypeAssertionExpression,
ast.KindExpressionWithTypeArguments,
ast.KindJsxSelfClosingElement,
ast.KindJsxSpreadAttribute,
ast.KindJsxExpression,
ast.KindPartiallyEmittedExpression:
// all immediate children that can be `Identifier` would be instances of `IdentifierReference`
return true
case ast.KindComputedPropertyName,
ast.KindDecorator,
ast.KindIfStatement,
ast.KindDoStatement,
ast.KindWhileStatement,
ast.KindWithStatement,
ast.KindReturnStatement,
ast.KindSwitchStatement,
ast.KindCaseClause,
ast.KindThrowStatement,
ast.KindExpressionStatement,
ast.KindExportAssignment,
ast.KindPropertyAccessExpression,
ast.KindTemplateSpan:
// only an `Expression()` child that can be `Identifier` would be an instance of `IdentifierReference`
return parent.Expression() == name
case ast.KindVariableDeclaration,
ast.KindParameter,
ast.KindBindingElement,
ast.KindPropertyDeclaration,
ast.KindPropertySignature,
ast.KindPropertyAssignment,
ast.KindEnumMember,
ast.KindJsxAttribute:
// only an `Initializer()` child that can be `Identifier` would be an instance of `IdentifierReference`
return parent.Initializer() == name
case ast.KindShorthandPropertyAssignment:
return parent.AsShorthandPropertyAssignment().ObjectAssignmentInitializer == name
case ast.KindForStatement:
return parent.Initializer() == name ||
parent.AsForStatement().Condition == name ||
parent.AsForStatement().Incrementor == name
case ast.KindForInStatement,
ast.KindForOfStatement:
return parent.Initializer() == name ||
parent.Expression() == name
case ast.KindImportEqualsDeclaration:
return parent.AsImportEqualsDeclaration().ModuleReference == name
case ast.KindArrowFunction:
return parent.Body() == name
case ast.KindConditionalExpression:
return parent.AsConditionalExpression().Condition == name ||
parent.AsConditionalExpression().WhenTrue == name ||
parent.AsConditionalExpression().WhenFalse == name
case ast.KindCallExpression, ast.KindNewExpression:
return parent.Expression() == name ||
slices.Contains(parent.Arguments(), name)
case ast.KindTaggedTemplateExpression:
return parent.AsTaggedTemplateExpression().Tag == name
case ast.KindImportAttribute:
return parent.AsImportAttribute().Value == name
case ast.KindJsxOpeningElement, ast.KindJsxClosingElement:
return parent.TagName() == name
default:
return false
}
}
func convertBindingElementToArrayAssignmentElement(emitContext *printer.EmitContext, element *ast.BindingElement) *ast.Expression {
if element.Name() == nil {
elision := emitContext.Factory.NewOmittedExpression()
emitContext.SetOriginal(elision, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(elision, element.AsNode())
return elision
}
if element.DotDotDotToken != nil {
spread := emitContext.Factory.NewSpreadElement(element.Name())
emitContext.SetOriginal(spread, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(spread, element.AsNode())
return spread
}
expression := convertBindingNameToAssignmentElementTarget(emitContext, element.Name())
if element.Initializer != nil {
assignment := emitContext.Factory.NewAssignmentExpression(expression, element.Initializer)
emitContext.SetOriginal(assignment, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(assignment, element.AsNode())
return assignment
}
return expression
}
func convertBindingElementToObjectAssignmentElement(emitContext *printer.EmitContext, element *ast.BindingElement) *ast.ObjectLiteralElement {
if element.DotDotDotToken != nil {
spread := emitContext.Factory.NewSpreadAssignment(element.Name())
emitContext.SetOriginal(spread, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(spread, element.AsNode())
return spread
}
if element.PropertyName != nil {
expression := convertBindingNameToAssignmentElementTarget(emitContext, element.Name())
if element.Initializer != nil {
expression = emitContext.Factory.NewAssignmentExpression(expression, element.Initializer)
}
assignment := emitContext.Factory.NewPropertyAssignment(nil /*modifiers*/, element.PropertyName, nil /*postfixToken*/, nil /*typeNode*/, expression)
emitContext.SetOriginal(assignment, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(assignment, element.AsNode())
return assignment
}
var equalsToken *ast.TokenNode
if element.Initializer != nil {
equalsToken = emitContext.Factory.NewToken(ast.KindEqualsToken)
}
assignment := emitContext.Factory.NewShorthandPropertyAssignment(
nil, /*modifiers*/
element.Name(),
nil, /*postfixToken*/
nil, /*typeNode*/
equalsToken,
element.Initializer,
)
emitContext.SetOriginal(assignment, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(assignment, element.AsNode())
return assignment
}
func ConvertBindingPatternToAssignmentPattern(emitContext *printer.EmitContext, element *ast.BindingPattern) *ast.Expression {
switch element.Kind {
case ast.KindArrayBindingPattern:
return convertBindingElementToArrayAssignmentPattern(emitContext, element)
case ast.KindObjectBindingPattern:
return convertBindingElementToObjectAssignmentPattern(emitContext, element)
default:
panic("Unknown binding pattern")
}
}
func convertBindingElementToObjectAssignmentPattern(emitContext *printer.EmitContext, element *ast.BindingPattern) *ast.Expression {
var properties []*ast.ObjectLiteralElement
for _, element := range element.Elements.Nodes {
properties = append(properties, convertBindingElementToObjectAssignmentElement(emitContext, element.AsBindingElement()))
}
propertyList := emitContext.Factory.NewNodeList(properties)
propertyList.Loc = element.Elements.Loc
object := emitContext.Factory.NewObjectLiteralExpression(propertyList, false /*multiLine*/)
emitContext.SetOriginal(object, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(object, element.AsNode())
return object
}
func convertBindingElementToArrayAssignmentPattern(emitContext *printer.EmitContext, element *ast.BindingPattern) *ast.Expression {
var elements []*ast.Expression
for _, element := range element.Elements.Nodes {
elements = append(elements, convertBindingElementToArrayAssignmentElement(emitContext, element.AsBindingElement()))
}
elementList := emitContext.Factory.NewNodeList(elements)
elementList.Loc = element.Elements.Loc
object := emitContext.Factory.NewArrayLiteralExpression(elementList, false /*multiLine*/)
emitContext.SetOriginal(object, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(object, element.AsNode())
return object
}
func convertBindingNameToAssignmentElementTarget(emitContext *printer.EmitContext, element *ast.Node) *ast.Expression {
if ast.IsBindingPattern(element) {
return ConvertBindingPatternToAssignmentPattern(emitContext, element.AsBindingPattern())
}
return element
}
func ConvertVariableDeclarationToAssignmentExpression(emitContext *printer.EmitContext, element *ast.VariableDeclaration) *ast.Expression {
if element.Initializer == nil {
return nil
}
expression := convertBindingNameToAssignmentElementTarget(emitContext, element.Name())
assignment := emitContext.Factory.NewAssignmentExpression(expression, element.Initializer)
emitContext.SetOriginal(assignment, element.AsNode())
emitContext.AssignCommentAndSourceMapRanges(assignment, element.AsNode())
return assignment
}
func SingleOrMany(nodes []*ast.Node, factory *printer.NodeFactory) *ast.Node {
if nodes == nil {
return nil
}
if len(nodes) == 1 {
return nodes[0]
}
return factory.NewSyntaxList(nodes)
}
// Used in the module transformer to check if an expression is reasonably without sideeffect,
//
// and thus better to copy into multiple places rather than to cache in a temporary variable
// - this is mostly subjective beyond the requirement that the expression not be sideeffecting
//
// Also used by the logical assignment downleveling transform to skip temp variables when they're
// not needed.
func IsSimpleCopiableExpression(expression *ast.Expression) bool {
return ast.IsStringLiteralLike(expression) ||
ast.IsNumericLiteral(expression) ||
ast.IsKeywordKind(expression.Kind) ||
ast.IsIdentifier(expression)
}
func IsOriginalNodeSingleLine(emitContext *printer.EmitContext, node *ast.Node) bool {
if node == nil {
return false
}
original := emitContext.MostOriginal(node)
if original == nil {
return false
}
source := ast.GetSourceFileOfNode(original)
if source == nil {
return false
}
startLine := scanner.GetECMALineOfPosition(source, original.Loc.Pos())
endLine := scanner.GetECMALineOfPosition(source, original.Loc.End())
return startLine == endLine
}
/**
* A simple inlinable expression is an expression which can be copied into multiple locations
* without risk of repeating any sideeffects and whose value could not possibly change between
* any such locations
*/
func IsSimpleInlineableExpression(expression *ast.Expression) bool {
return !ast.IsIdentifier(expression) && IsSimpleCopiableExpression(expression)
}
// FindSuperStatementIndexPath finds a path of indices to a statement containing a `super()` call.
func FindSuperStatementIndexPath(statements []*ast.Statement, start int) []int {
indices := findSuperStatementIndexPathWorker(statements, start, nil)
slices.Reverse(indices)
return indices
}
func findSuperStatementIndexPathWorker(statements []*ast.Statement, start int, indices []int) []int {
for i := start; i < len(statements); i++ {
statement := statements[i]
if GetSuperCallFromStatement(statement) != nil {
return append(indices, i)
} else if ast.IsTryStatement(statement) {
if result := findSuperStatementIndexPathWorker(statement.AsTryStatement().TryBlock.Statements(), 0, indices); result != nil {
return append(result, i)
}
}
}
return nil
}
// GetSuperCallFromStatement extracts the super() call expression from an expression statement, if any.
func GetSuperCallFromStatement(statement *ast.Statement) *ast.Node {
if !ast.IsExpressionStatement(statement) {
return nil
}
expression := ast.SkipParentheses(statement.Expression())
if ast.IsSuperCall(expression) {
return expression
}
return nil
}
// MoveRangePastModifiers returns a text range that starts past any modifiers on the node.
func MoveRangePastModifiers(node *ast.Node) core.TextRange {
if ast.IsPropertyDeclaration(node) || ast.IsMethodDeclaration(node) {
return core.NewTextRange(node.Name().Pos(), node.End())
}
var lastModifier *ast.Node
if ast.CanHaveModifiers(node) {
lastModifier = core.LastOrNil(node.ModifierNodes())
}
if lastModifier != nil && !ast.PositionIsSynthesized(lastModifier.End()) {
return core.NewTextRange(lastModifier.End(), node.End())
}
return MoveRangePastDecorators(node)
}
// MoveRangePastDecorators returns a text range that starts past any decorators on the node.
func MoveRangePastDecorators(node *ast.Node) core.TextRange {
var lastDecorator *ast.Node
if ast.CanHaveModifiers(node) {
nodes := node.ModifierNodes()
if nodes != nil {
lastDecorator = core.FindLast(nodes, ast.IsDecorator)
}
}
if lastDecorator != nil && !ast.PositionIsSynthesized(lastDecorator.End()) {
return core.NewTextRange(lastDecorator.End(), node.End())
}
return node.Loc
}
// GetNonAssignmentOperatorForCompoundAssignment returns the non-assignment operator for a compound assignment.
func GetNonAssignmentOperatorForCompoundAssignment(kind ast.Kind) ast.Kind {
switch kind {
case ast.KindPlusEqualsToken:
return ast.KindPlusToken
case ast.KindMinusEqualsToken:
return ast.KindMinusToken
case ast.KindAsteriskEqualsToken:
return ast.KindAsteriskToken
case ast.KindAsteriskAsteriskEqualsToken:
return ast.KindAsteriskAsteriskToken
case ast.KindSlashEqualsToken:
return ast.KindSlashToken
case ast.KindPercentEqualsToken:
return ast.KindPercentToken
case ast.KindLessThanLessThanEqualsToken:
return ast.KindLessThanLessThanToken
case ast.KindGreaterThanGreaterThanEqualsToken:
return ast.KindGreaterThanGreaterThanToken
case ast.KindGreaterThanGreaterThanGreaterThanEqualsToken:
return ast.KindGreaterThanGreaterThanGreaterThanToken
case ast.KindAmpersandEqualsToken:
return ast.KindAmpersandToken
case ast.KindBarEqualsToken:
return ast.KindBarToken
case ast.KindCaretEqualsToken:
return ast.KindCaretToken
case ast.KindBarBarEqualsToken:
return ast.KindBarBarToken
case ast.KindAmpersandAmpersandEqualsToken:
return ast.KindAmpersandAmpersandToken
case ast.KindQuestionQuestionEqualsToken:
return ast.KindQuestionQuestionToken
}
return kind
}