vendor tsgo
This commit is contained in:
62
tools/tsgo/internal/transformers/chain.go
Normal file
62
tools/tsgo/internal/transformers/chain.go
Normal 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)
|
||||
}
|
||||
}
|
||||
735
tools/tsgo/internal/transformers/declarations/diagnostics.go
Normal file
735
tools/tsgo/internal/transformers/declarations/diagnostics.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
252
tools/tsgo/internal/transformers/declarations/tracker.go
Normal file
252
tools/tsgo/internal/transformers/declarations/tracker.go
Normal 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
|
||||
}
|
||||
2957
tools/tsgo/internal/transformers/declarations/transform.go
Normal file
2957
tools/tsgo/internal/transformers/declarations/transform.go
Normal file
File diff suppressed because it is too large
Load Diff
186
tools/tsgo/internal/transformers/declarations/util.go
Normal file
186
tools/tsgo/internal/transformers/declarations/util.go
Normal 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)
|
||||
}
|
||||
510
tools/tsgo/internal/transformers/destructuring.go
Normal file
510
tools/tsgo/internal/transformers/destructuring.go
Normal 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)
|
||||
}
|
||||
995
tools/tsgo/internal/transformers/estransforms/async.go
Normal file
995
tools/tsgo/internal/transformers/estransforms/async.go
Normal 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
|
||||
}
|
||||
3618
tools/tsgo/internal/transformers/estransforms/classfields.go
Normal file
3618
tools/tsgo/internal/transformers/estransforms/classfields.go
Normal file
File diff suppressed because it is too large
Load Diff
28
tools/tsgo/internal/transformers/estransforms/classthis.go
Normal file
28
tools/tsgo/internal/transformers/estransforms/classthis.go
Normal 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
|
||||
}
|
||||
43
tools/tsgo/internal/transformers/estransforms/definitions.go
Normal file
43
tools/tsgo/internal/transformers/estransforms/definitions.go
Normal 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)
|
||||
}
|
||||
}
|
||||
2751
tools/tsgo/internal/transformers/estransforms/esdecorator.go
Normal file
2751
tools/tsgo/internal/transformers/estransforms/esdecorator.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
856
tools/tsgo/internal/transformers/estransforms/forawait.go
Normal file
856
tools/tsgo/internal/transformers/estransforms/forawait.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
537
tools/tsgo/internal/transformers/estransforms/namedevaluation.go
Normal file
537
tools/tsgo/internal/transformers/estransforms/namedevaluation.go
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
240
tools/tsgo/internal/transformers/estransforms/optionalchain.go
Normal file
240
tools/tsgo/internal/transformers/estransforms/optionalchain.go
Normal 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)
|
||||
}
|
||||
174
tools/tsgo/internal/transformers/estransforms/taggedtemplate.go
Normal file
174
tools/tsgo/internal/transformers/estransforms/taggedtemplate.go
Normal 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
|
||||
}
|
||||
50
tools/tsgo/internal/transformers/estransforms/usestrict.go
Normal file
50
tools/tsgo/internal/transformers/estransforms/usestrict.go
Normal 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()
|
||||
}
|
||||
799
tools/tsgo/internal/transformers/estransforms/using.go
Normal file
799
tools/tsgo/internal/transformers/estransforms/using.go
Normal 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
|
||||
}
|
||||
289
tools/tsgo/internal/transformers/estransforms/utilities.go
Normal file
289
tools/tsgo/internal/transformers/estransforms/utilities.go
Normal 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,
|
||||
)
|
||||
}
|
||||
102
tools/tsgo/internal/transformers/inliners/constenum.go
Normal file
102
tools/tsgo/internal/transformers/inliners/constenum.go
Normal 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()
|
||||
}
|
||||
1209
tools/tsgo/internal/transformers/jsxtransforms/jsx.go
Normal file
1209
tools/tsgo/internal/transformers/jsxtransforms/jsx.go
Normal file
File diff suppressed because it is too large
Load Diff
28
tools/tsgo/internal/transformers/modifiervisitor.go
Normal file
28
tools/tsgo/internal/transformers/modifiervisitor.go
Normal 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)
|
||||
}
|
||||
2162
tools/tsgo/internal/transformers/moduletransforms/commonjsmodule.go
Normal file
2162
tools/tsgo/internal/transformers/moduletransforms/commonjsmodule.go
Normal file
File diff suppressed because it is too large
Load Diff
372
tools/tsgo/internal/transformers/moduletransforms/esmodule.go
Normal file
372
tools/tsgo/internal/transformers/moduletransforms/esmodule.go
Normal 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,
|
||||
)
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
118
tools/tsgo/internal/transformers/moduletransforms/utilities.go
Normal file
118
tools/tsgo/internal/transformers/moduletransforms/utilities.go
Normal 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)
|
||||
}
|
||||
41
tools/tsgo/internal/transformers/transformer.go
Normal file
41
tools/tsgo/internal/transformers/transformer.go
Normal 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)
|
||||
}
|
||||
153
tools/tsgo/internal/transformers/tstransforms/importelision.go
Normal file
153
tools/tsgo/internal/transformers/tstransforms/importelision.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
1038
tools/tsgo/internal/transformers/tstransforms/legacydecorators.go
Normal file
1038
tools/tsgo/internal/transformers/tstransforms/legacydecorators.go
Normal file
File diff suppressed because it is too large
Load Diff
390
tools/tsgo/internal/transformers/tstransforms/metadata.go
Normal file
390
tools/tsgo/internal/transformers/tstransforms/metadata.go
Normal 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
|
||||
}
|
||||
995
tools/tsgo/internal/transformers/tstransforms/runtimesyntax.go
Normal file
995
tools/tsgo/internal/transformers/tstransforms/runtimesyntax.go
Normal 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
|
||||
}
|
||||
393
tools/tsgo/internal/transformers/tstransforms/typeeraser.go
Normal file
393
tools/tsgo/internal/transformers/tstransforms/typeeraser.go
Normal 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)
|
||||
}
|
||||
}
|
||||
106
tools/tsgo/internal/transformers/tstransforms/typeeraser_test.go
Normal file
106
tools/tsgo/internal/transformers/tstransforms/typeeraser_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
511
tools/tsgo/internal/transformers/tstransforms/typeserializer.go
Normal file
511
tools/tsgo/internal/transformers/tstransforms/typeserializer.go
Normal 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
|
||||
}
|
||||
29
tools/tsgo/internal/transformers/tstransforms/utilities.go
Normal file
29
tools/tsgo/internal/transformers/tstransforms/utilities.go
Normal 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
|
||||
}
|
||||
375
tools/tsgo/internal/transformers/utilities.go
Normal file
375
tools/tsgo/internal/transformers/utilities.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user