diff --git a/kitcom/internal/tsgo/transformers/chain.go b/kitcom/internal/tsgo/transformers/chain.go deleted file mode 100644 index 481a142..0000000 --- a/kitcom/internal/tsgo/transformers/chain.go +++ /dev/null @@ -1,54 +0,0 @@ -package transformers - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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? - constructed = append(constructed, t(opt)) - } - ch := &chainedTransformer{components: constructed} - return ch.NewTransformer(ch.visit, opt.Context) - } -} diff --git a/kitcom/internal/tsgo/transformers/declarations/diagnostics.go b/kitcom/internal/tsgo/transformers/declarations/diagnostics.go deleted file mode 100644 index 729c739..0000000 --- a/kitcom/internal/tsgo/transformers/declarations/diagnostics.go +++ /dev/null @@ -1,451 +0,0 @@ -package declarations - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -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.IsParameter(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.AsTypeAliasDeclaration().Type - typeName := node.Name() - 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()) - } -} - -// !!! TODO isolatedDeclarations createGetIsolatedDeclarationErrors diff --git a/kitcom/internal/tsgo/transformers/declarations/tracker.go b/kitcom/internal/tsgo/transformers/declarations/tracker.go deleted file mode 100644 index 42143ec..0000000 --- a/kitcom/internal/tsgo/transformers/declarations/tracker.go +++ /dev/null @@ -1,219 +0,0 @@ -package declarations - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" -) - -type SymbolTrackerImpl struct { - resolver printer.EmitResolver - state *SymbolTrackerSharedState - host DeclarationEmitHost - fallbackStack []*ast.Node -} - -// GetModuleSpecifierGenerationHost implements checker.SymbolTracker. -func (s *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() modulespecifiers.ModuleSpecifierGenerationHost { - return s.host -} - -// 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")) - } -} - -// ReportInferenceFallback implements checker.SymbolTracker. -func (s *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) { - if s.state.isolatedDeclarations || ast.IsSourceFileJS(s.state.currentSourceFile) { - 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 ast.IsVariableDeclaration(node) && s.state.resolver.IsExpandoFunctionDeclaration(node) { - s.state.reportExpandoFunctionErrors(node) - } else { - // !!! isolatedDeclaration support - // s.state.addDiagnostic(getIsolatedDeclarationError(node)) - } -} - -// ReportLikelyUnsafeImportRequiredError implements checker.SymbolTracker. -func (s *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) { - location := s.errorLocation() - if location != nil { - 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 - } - 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 - 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} - return tracker -} diff --git a/kitcom/internal/tsgo/transformers/declarations/transform.go b/kitcom/internal/tsgo/transformers/declarations/transform.go deleted file mode 100644 index ba53c32..0000000 --- a/kitcom/internal/tsgo/transformers/declarations/transform.go +++ /dev/null @@ -1,1980 +0,0 @@ -package declarations - -import ( - "fmt" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type ReferencedFilePair struct { - file *ast.SourceFile - ref *ast.FileReference -} - -type OutputPaths interface { - DeclarationFilePath() string - JsFilePath() string -} - -// Used to be passed in the TransformationContext, which is now just an EmitContext -type DeclarationEmitHost interface { - modulespecifiers.ModuleSpecifierGenerationHost - GetCurrentDirectory() string - UseCaseSensitiveFileNames() bool - GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile - - GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths - GetResolutionModeOverride(node *ast.Node) core.ResolutionMode - GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags - GetEmitResolver() printer.EmitResolver -} - -type DeclarationTransformer struct { - transformers.Transformer - host DeclarationEmitHost - compilerOptions *core.CompilerOptions - tracker *SymbolTrackerImpl - state *SymbolTrackerSharedState - resolver printer.EmitResolver - declarationFilePath string - declarationMapPath string - - isBundledEmit bool - needsDeclare bool - needsScopeFixMarker bool - resultHasScopeMarker bool - enclosingDeclaration *ast.Node - resultHasExternalModuleIndicator bool - suppressNewDiagnosticContexts bool - lateStatementReplacementMap map[ast.NodeId]*ast.Node - expandoHosts collections.Set[ast.NodeId] - rawReferencedFiles []ReferencedFilePair - rawTypeReferenceDirectives []*ast.FileReference - rawLibReferenceDirectives []*ast.FileReference -} - -// TODO: Convert to transformers.TransformerFactory signature to allow more automatic composition with other transforms -func NewDeclarationTransformer(host DeclarationEmitHost, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer { - resolver := host.GetEmitResolver() - state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver} - tracker := NewSymbolTracker(host, resolver, state) - // TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit) - tx := &DeclarationTransformer{ - host: host, - compilerOptions: compilerOptions, - tracker: tracker, - state: state, - resolver: resolver, - declarationFilePath: declarationFilePath, - declarationMapPath: declarationMapPath, - } - tx.NewTransformer(tx.visit, context) - return tx -} - -func (tx *DeclarationTransformer) GetDiagnostics() []*ast.Diagnostic { - return tx.state.diagnostics -} - -const declarationEmitNodeBuilderFlags = nodebuilder.FlagsMultilineObjectLiterals | - nodebuilder.FlagsWriteClassExpressionAsTypeLiteral | - nodebuilder.FlagsUseTypeOfFunction | - nodebuilder.FlagsUseStructuralFallback | - nodebuilder.FlagsAllowEmptyTuple | - nodebuilder.FlagsGenerateNamesForShadowedTypeParams | - nodebuilder.FlagsNoTruncation - -const declarationEmitInternalNodeBuilderFlags = nodebuilder.InternalFlagsAllowUnresolvedNames - -// functions as both `visitDeclarationStatements` and `transformRoot`, utilitzing SyntaxList nodes -func (tx *DeclarationTransformer) visit(node *ast.Node) *ast.Node { - if node == nil { - return nil - } - // !!! TODO: Bundle support? - switch node.Kind { - case ast.KindSourceFile: - return tx.visitSourceFile(node.AsSourceFile()) - // statements we keep but do something to - case ast.KindFunctionDeclaration, - ast.KindModuleDeclaration, - ast.KindImportEqualsDeclaration, - ast.KindInterfaceDeclaration, - ast.KindClassDeclaration, - ast.KindJSTypeAliasDeclaration, - ast.KindTypeAliasDeclaration, - ast.KindEnumDeclaration, - ast.KindVariableStatement, - ast.KindImportDeclaration, - ast.KindJSImportDeclaration, - ast.KindExportDeclaration, - ast.KindJSExportAssignment, - ast.KindExportAssignment: - return tx.visitDeclarationStatements(node) - // statements we elide - case ast.KindBreakStatement, - ast.KindContinueStatement, - ast.KindDebuggerStatement, - ast.KindDoStatement, - ast.KindEmptyStatement, - ast.KindForInStatement, - ast.KindForOfStatement, - ast.KindForStatement, - ast.KindIfStatement, - ast.KindLabeledStatement, - ast.KindReturnStatement, - ast.KindSwitchStatement, - ast.KindThrowStatement, - ast.KindTryStatement, - ast.KindWhileStatement, - ast.KindWithStatement, - ast.KindNotEmittedStatement, - ast.KindBlock, - ast.KindMissingDeclaration: - return nil - case ast.KindExpressionStatement: - return tx.visitExpressionStatement(node.AsExpressionStatement()) - // parts of things, things we just visit children of - default: - return tx.visitDeclarationSubtree(node) - } -} - -func throwDiagnostic(result printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { - panic("Diagnostic emitted without context") -} - -func (tx *DeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node { - if node.IsDeclarationFile { - return node.AsNode() - } - - tx.isBundledEmit = false - tx.needsDeclare = true - tx.needsScopeFixMarker = false - tx.resultHasScopeMarker = false - tx.enclosingDeclaration = node.AsNode() - tx.state.getSymbolAccessibilityDiagnostic = throwDiagnostic - tx.resultHasExternalModuleIndicator = false - tx.suppressNewDiagnosticContexts = false - tx.state.lateMarkedStatements = make([]*ast.Node, 0) - tx.lateStatementReplacementMap = make(map[ast.NodeId]*ast.Node) - tx.expandoHosts = collections.Set[ast.NodeId]{} - tx.rawReferencedFiles = make([]ReferencedFilePair, 0) - tx.rawTypeReferenceDirectives = make([]*ast.FileReference, 0) - tx.rawLibReferenceDirectives = make([]*ast.FileReference, 0) - tx.state.currentSourceFile = node - tx.collectFileReferences(node) - tx.resolver.PrecalculateDeclarationEmitVisibility(node) - updated := tx.transformSourceFile(node) - tx.state.currentSourceFile = nil - return updated -} - -func (tx *DeclarationTransformer) collectFileReferences(sourceFile *ast.SourceFile) { - tx.rawReferencedFiles = append(tx.rawReferencedFiles, core.Map(sourceFile.ReferencedFiles, func(ref *ast.FileReference) ReferencedFilePair { return ReferencedFilePair{file: sourceFile, ref: ref} })...) - tx.rawTypeReferenceDirectives = append(tx.rawTypeReferenceDirectives, sourceFile.TypeReferenceDirectives...) - tx.rawLibReferenceDirectives = append(tx.rawLibReferenceDirectives, sourceFile.LibReferenceDirectives...) -} - -func (tx *DeclarationTransformer) transformSourceFile(node *ast.SourceFile) *ast.Node { - var combinedStatements *ast.StatementList - statements := tx.Visitor().VisitNodes(node.Statements) - combinedStatements = tx.transformAndReplaceLatePaintedStatements(statements) - combinedStatements.Loc = statements.Loc // setTextRange - if ast.IsExternalOrCommonJSModule(node) && (!tx.resultHasExternalModuleIndicator || (tx.needsScopeFixMarker && !tx.resultHasScopeMarker)) { - marker := createEmptyExports(tx.Factory().AsNodeFactory()) - newList := append(combinedStatements.Nodes, marker) - withMarker := tx.Factory().NewNodeList(newList) - withMarker.Loc = combinedStatements.Loc - combinedStatements = withMarker - } - outputFilePath := tspath.GetDirectoryPath(tspath.NormalizeSlashes(tx.declarationFilePath)) - result := tx.Factory().UpdateSourceFile(node, combinedStatements, node.EndOfFileToken) - result.AsSourceFile().LibReferenceDirectives = tx.getLibReferences() - result.AsSourceFile().TypeReferenceDirectives = tx.getTypeReferences() - result.AsSourceFile().IsDeclarationFile = true - result.AsSourceFile().ReferencedFiles = tx.getReferencedFiles(outputFilePath) - return result.AsNode() -} - -func createEmptyExports(factory *ast.NodeFactory) *ast.Node { - return factory.NewExportDeclaration(nil /*isTypeOnly*/, false, factory.NewNamedExports(factory.NewNodeList([]*ast.Node{})), nil, nil) -} - -func (tx *DeclarationTransformer) transformAndReplaceLatePaintedStatements(statements *ast.StatementList) *ast.StatementList { - // This is a `while` loop because `handleSymbolAccessibilityError` can see additional import aliases marked as visible during - // error handling which must now be included in the output and themselves checked for errors. - // For example: - // ``` - // module A { - // export module Q {} - // import B = Q; - // import C = B; - // export import D = C; - // } - // ``` - // In such a scenario, only Q and D are initially visible, but we don't consider imports as private names - instead we say they if they are referenced they must - // be recorded. So while checking D's visibility we mark C as visible, then we must check C which in turn marks B, completing the chain of - // dependent imports and allowing a valid declaration file output. Today, this dependent alias marking only happens for internal import aliases. - for true { - if len(tx.state.lateMarkedStatements) == 0 { - break - } - - next := tx.state.lateMarkedStatements[0] - tx.state.lateMarkedStatements = tx.state.lateMarkedStatements[1:] - - saveNeedsDeclare := tx.needsDeclare - tx.needsDeclare = next.Parent != nil && ast.IsSourceFile(next.Parent) && !(ast.IsExternalModule(next.Parent.AsSourceFile()) && tx.isBundledEmit) - - result := tx.transformTopLevelDeclaration(next) - - tx.needsDeclare = saveNeedsDeclare - original := tx.EmitContext().MostOriginal(next) - id := ast.GetNodeId(original) - tx.lateStatementReplacementMap[id] = result - } - - // And lastly, we need to get the final form of all those indetermine import declarations from before and add them to the output list - // (and remove them from the set to examine for outter declarations) - results := make([]*ast.Node, 0, len(statements.Nodes)) - for _, statement := range statements.Nodes { - if !ast.IsLateVisibilityPaintedStatement(statement) { - results = append(results, statement) - continue - } - original := tx.EmitContext().MostOriginal(statement) - id := ast.GetNodeId(original) - replacement, ok := tx.lateStatementReplacementMap[id] - if !ok { - results = append(results, statement) - continue // not replaced - } - if replacement == nil { - continue // deleted - } - if replacement.Kind == ast.KindSyntaxList { - if !tx.needsScopeFixMarker || !tx.resultHasExternalModuleIndicator { - for _, elem := range replacement.AsSyntaxList().Children { - if needsScopeMarker(elem) { - tx.needsScopeFixMarker = true - } - if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) { - tx.resultHasExternalModuleIndicator = true - } - } - } - results = append(results, replacement.AsSyntaxList().Children...) - } else { - if needsScopeMarker(replacement) { - tx.needsScopeFixMarker = true - } - if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) { - tx.resultHasExternalModuleIndicator = true - } - results = append(results, replacement) - } - } - - return tx.Factory().NewNodeList(results) -} - -func (tx *DeclarationTransformer) getReferencedFiles(outputFilePath string) (results []*ast.FileReference) { - // Handle path rewrites for triple slash ref comments - for _, pair := range tx.rawReferencedFiles { - sourceFile := pair.file - ref := pair.ref - - if !ref.Preserve { - continue - } - - file := tx.host.GetSourceFileFromReference(sourceFile, ref) - if file == nil { - continue - } - - var declFileName string - if file.IsDeclarationFile { - declFileName = file.FileName() - } else { - // !!! bundled emit support, omit bundled refs - // if (tx.isBundledEmit && contains((node as Bundle).sourceFiles, file)) continue - paths := tx.host.GetOutputPathsFor(file, true) - // Try to use output path for referenced file, or output js path if that doesn't exist, or the input path if all else fails - declFileName = paths.DeclarationFilePath() - if len(declFileName) == 0 { - declFileName = paths.JsFilePath() - } - if len(declFileName) == 0 { - declFileName = file.FileName() - } - } - // Should only be missing if the source file is missing a fileName (at which point we can't name a reference to it anyway) - // TODO: Shouldn't this be a crash or assert instead of a silent continue? - if len(declFileName) == 0 { - continue - } - - fileName := tspath.GetRelativePathToDirectoryOrUrl( - outputFilePath, - declFileName, - false, // TODO: Probably unsafe to assume this isn't a URL, but that's what strada does - tspath.ComparePathsOptions{ - CurrentDirectory: tx.host.GetCurrentDirectory(), - UseCaseSensitiveFileNames: tx.host.UseCaseSensitiveFileNames(), - }, - ) - - results = append(results, &ast.FileReference{ - TextRange: core.NewTextRange(-1, -1), - FileName: fileName, - ResolutionMode: ref.ResolutionMode, - Preserve: ref.Preserve, - }) - } - return results -} - -func (tx *DeclarationTransformer) getLibReferences() (result []*ast.FileReference) { - // clone retained references - for _, ref := range tx.rawLibReferenceDirectives { - if !ref.Preserve { - continue - } - result = append(result, &ast.FileReference{ - TextRange: core.NewTextRange(-1, -1), - FileName: ref.FileName, - ResolutionMode: ref.ResolutionMode, - Preserve: ref.Preserve, - }) - } - return result -} - -func (tx *DeclarationTransformer) getTypeReferences() (result []*ast.FileReference) { - // clone retained references - for _, ref := range tx.rawTypeReferenceDirectives { - if !ref.Preserve { - continue - } - result = append(result, &ast.FileReference{ - TextRange: core.NewTextRange(-1, -1), - FileName: ref.FileName, - ResolutionMode: ref.ResolutionMode, - Preserve: ref.Preserve, - }) - } - return result -} - -func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast.Node { - // !!! TODO: stripInternal support? - // if (shouldStripInternal(input)) return nil - if ast.IsDeclaration(input) { - if isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) { - return nil - } - if ast.HasDynamicName(input) { - if tx.state.isolatedDeclarations { - // Classes and object literals usually elide properties with computed names that are not of a literal type - // In isolated declarations TSC needs to error on these as we don't know the type in a DTE. - if !tx.resolver.IsDefinitelyReferenceToGlobalSymbolObject(input.Name().Expression()) { - if ast.IsClassDeclaration(input.Parent) || ast.IsObjectLiteralExpression(input.Parent) { - // !!! TODO: isolatedDeclarations diagnostics - // context.addDiagnostic(createDiagnosticForNode(input, diagnostics.Computed_property_names_on_class_or_object_literals_cannot_be_inferred_with_isolatedDeclarations)) - return nil - } else if (ast.IsInterfaceDeclaration(input.Parent) || ast.IsTypeLiteralNode(input.Parent)) && !ast.IsEntityNameExpression(input.Name().Expression()) { - // Type declarations just need to double-check that the input computed name is an entity name expression - // !!! TODO: isolatedDeclarations diagnostics - // context.addDiagnostic(createDiagnosticForNode(input, diagnostics.Computed_properties_must_be_number_or_string_literals_variables_or_dotted_expressions_with_isolatedDeclarations)) - return nil - } - } - } else if !tx.resolver.IsLateBound(tx.EmitContext().ParseNode(input)) || !ast.IsEntityNameExpression(input.Name().AsComputedPropertyName().Expression) { - return nil - } - } - } - - // Elide implementation signatures from overload sets - if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) { - return nil - } - - if input.Kind == ast.KindSemicolonClassElement { - return nil - } - - previousEnclosingDeclaration := tx.enclosingDeclaration - if isEnclosingDeclaration(input) { - tx.enclosingDeclaration = input - } - - canProdiceDiagnostic := canProduceDiagnostics(input) - oldWithinObjectLiteralType := tx.suppressNewDiagnosticContexts - shouldEnterSuppressNewDiagnosticsContextContext := (input.Kind == ast.KindTypeLiteral || input.Kind == ast.KindMappedType) && !(input.Parent.Kind == ast.KindTypeAliasDeclaration || input.Parent.Kind == ast.KindJSTypeAliasDeclaration) - - oldDiag := tx.state.getSymbolAccessibilityDiagnostic - if canProdiceDiagnostic && !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) - } - oldName := tx.state.errorNameNode - - if shouldEnterSuppressNewDiagnosticsContextContext { - tx.suppressNewDiagnosticContexts = true - } - - var result *ast.Node - - switch input.Kind { - case ast.KindMappedType: - result = tx.transformMappedTypeNode(input.AsMappedTypeNode()) - case ast.KindHeritageClause: - result = tx.transformHeritageClause(input.AsHeritageClause()) - case ast.KindMethodSignature: - result = tx.transformMethodSignatureDeclaration(input.AsMethodSignatureDeclaration()) - case ast.KindMethodDeclaration: - result = tx.transformMethodDeclaration(input.AsMethodDeclaration()) - case ast.KindConstructSignature: - result = tx.transformConstructSignatureDeclaration(input.AsConstructSignatureDeclaration()) - case ast.KindConstructor: - result = tx.transformConstructorDeclaration(input.AsConstructorDeclaration()) - case ast.KindGetAccessor: - result = tx.transformGetAccesorDeclaration(input.AsGetAccessorDeclaration()) - case ast.KindSetAccessor: - result = tx.transformSetAccessorDeclaration(input.AsSetAccessorDeclaration()) - case ast.KindPropertyDeclaration: - result = tx.transformPropertyDeclaration(input.AsPropertyDeclaration()) - case ast.KindPropertySignature: - result = tx.transformPropertySignatureDeclaration(input.AsPropertySignatureDeclaration()) - case ast.KindCallSignature: - result = tx.transformCallSignatureDeclaration(input.AsCallSignatureDeclaration()) - case ast.KindIndexSignature: - result = tx.transformIndexSignatureDeclaration(input.AsIndexSignatureDeclaration()) - case ast.KindVariableDeclaration: - result = tx.transformVariableDeclaration(input.AsVariableDeclaration()) - case ast.KindTypeParameter: - result = tx.transformTypeParameterDeclaration(input.AsTypeParameter()) - case ast.KindExpressionWithTypeArguments: - result = tx.transformExpressionWithTypeArguments(input.AsExpressionWithTypeArguments()) - case ast.KindTypeReference: - result = tx.transformTypeReference(input.AsTypeReference()) - case ast.KindConditionalType: - result = tx.transformConditionalTypeNode(input.AsConditionalTypeNode()) - case ast.KindFunctionType: - result = tx.transformFunctionTypeNode(input.AsFunctionTypeNode()) - case ast.KindConstructorType: - result = tx.transformConstructorTypeNode(input.AsConstructorTypeNode()) - case ast.KindImportType: - result = tx.transformImportTypeNode(input.AsImportTypeNode()) - case ast.KindTypeQuery: - tx.checkEntityNameVisibility(input.AsTypeQueryNode().ExprName, tx.enclosingDeclaration) - result = tx.Visitor().VisitEachChild(input) - case ast.KindTupleType: - result = tx.Visitor().VisitEachChild(input) - if result != nil { - if transformers.IsOriginalNodeSingleLine(tx.EmitContext(), input) { - tx.EmitContext().AddEmitFlags(result, printer.EFSingleLine) - } - } - case ast.KindJSDocTypeExpression: - result = tx.transformJSDocTypeExpression(input.AsJSDocTypeExpression()) - case ast.KindJSDocTypeLiteral: - result = tx.transformJSDocTypeLiteral(input.AsJSDocTypeLiteral()) - case ast.KindJSDocPropertyTag: - result = tx.transformJSDocPropertyTag(input.AsJSDocParameterOrPropertyTag()) - case ast.KindJSDocAllType: - result = tx.transformJSDocAllType(input.AsJSDocAllType()) - case ast.KindJSDocNullableType: - result = tx.transformJSDocNullableType(input.AsJSDocNullableType()) - case ast.KindJSDocNonNullableType: - result = tx.transformJSDocNonNullableType(input.AsJSDocNonNullableType()) - case ast.KindJSDocOptionalType: - result = tx.transformJSDocOptionalType(input.AsJSDocOptionalType()) - case ast.KindJSDocVariadicType: - result = tx.transformJSDocVariadicType(input.AsJSDocVariadicType()) - default: - result = tx.Visitor().VisitEachChild(input) - } - - if result != nil && canProdiceDiagnostic && ast.HasDynamicName(input) { - tx.checkName(input) - } - - tx.enclosingDeclaration = previousEnclosingDeclaration - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - tx.state.errorNameNode = oldName - tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType - return result -} - -func (tx *DeclarationTransformer) checkName(node *ast.Node) { - oldDiag := tx.state.getSymbolAccessibilityDiagnostic - if !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNodeName(node) - } - tx.state.errorNameNode = node.Name() - debug.Assert(ast.HasDynamicName(node)) // Should only be called with dynamic names - entityName := node.Name().AsComputedPropertyName().Expression - tx.checkEntityNameVisibility(entityName, tx.enclosingDeclaration) - if !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - } - tx.state.errorNameNode = nil -} - -func (tx *DeclarationTransformer) transformMappedTypeNode(input *ast.MappedTypeNode) *ast.Node { - // handle missing template type nodes, since the printer does not - var typeNode *ast.Node - if input.Type == nil { - typeNode = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) - } else { - typeNode = tx.Visitor().Visit(input.Type) - } - return tx.Factory().UpdateMappedTypeNode( - input, - input.ReadonlyToken, - tx.Visitor().Visit(input.TypeParameter), - tx.Visitor().Visit(input.NameType), - input.QuestionToken, - typeNode, - nil, - ) -} - -func (tx *DeclarationTransformer) transformHeritageClause(clause *ast.HeritageClause) *ast.Node { - retainedClauses := core.Filter(clause.Types.Nodes, func(t *ast.Node) bool { - return ast.IsEntityNameExpression(t.AsExpressionWithTypeArguments().Expression) || - (clause.Token == ast.KindExtendsKeyword && t.Expression().Kind == ast.KindNullKeyword) - }) - if len(retainedClauses) == 0 { - return nil // elide empty clause - } - if len(retainedClauses) == len(clause.Types.Nodes) { - return tx.Visitor().VisitEachChild(clause.AsNode()) - } - return tx.Factory().UpdateHeritageClause( - clause, - tx.Visitor().VisitNodes(tx.Factory().NewNodeList(retainedClauses)), - ) -} - -func (tx *DeclarationTransformer) transformImportTypeNode(input *ast.ImportTypeNode) *ast.Node { - if !ast.IsLiteralImportTypeNode(input.AsNode()) { - return input.AsNode() - } - return tx.Factory().UpdateImportTypeNode( - input, - input.IsTypeOf, - tx.Factory().UpdateLiteralTypeNode( - input.Argument.AsLiteralTypeNode(), - tx.rewriteModuleSpecifier(input.AsNode(), input.Argument.AsLiteralTypeNode().Literal), - ), - input.Attributes, - input.Qualifier, - tx.Visitor().VisitNodes(input.TypeArguments), - ) -} - -func (tx *DeclarationTransformer) transformConstructorTypeNode(input *ast.ConstructorTypeNode) *ast.Node { - return tx.Factory().UpdateConstructorTypeNode( - input, - tx.ensureModifiers(input.AsNode()), - tx.Visitor().VisitNodes(input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.Visitor().Visit(input.Type), - ) -} - -func (tx *DeclarationTransformer) transformFunctionTypeNode(input *ast.FunctionTypeNode) *ast.Node { - return tx.Factory().UpdateFunctionTypeNode( - input, - tx.Visitor().VisitNodes(input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.Visitor().Visit(input.Type), - ) -} - -func (tx *DeclarationTransformer) transformConditionalTypeNode(input *ast.ConditionalTypeNode) *ast.Node { - checkType := tx.Visitor().Visit(input.CheckType) - extendsType := tx.Visitor().Visit(input.ExtendsType) - oldEnclosingDecl := tx.enclosingDeclaration - tx.enclosingDeclaration = input.TrueType - trueType := tx.Visitor().Visit(input.TrueType) - tx.enclosingDeclaration = oldEnclosingDecl - falseType := tx.Visitor().Visit(input.FalseType) - - return tx.Factory().UpdateConditionalTypeNode( - input, - checkType, - extendsType, - trueType, - falseType, - ) -} - -func (tx *DeclarationTransformer) transformTypeReference(input *ast.TypeReferenceNode) *ast.Node { - tx.checkEntityNameVisibility(input.TypeName, tx.enclosingDeclaration) - return tx.Visitor().VisitEachChild(input.AsNode()) -} - -func (tx *DeclarationTransformer) transformExpressionWithTypeArguments(input *ast.ExpressionWithTypeArguments) *ast.Node { - if ast.IsEntityName(input.Expression) || ast.IsEntityNameExpression(input.Expression) { - tx.checkEntityNameVisibility(input.Expression, tx.enclosingDeclaration) - } - return tx.Visitor().VisitEachChild(input.AsNode()) -} - -func (tx *DeclarationTransformer) transformTypeParameterDeclaration(input *ast.TypeParameterDeclaration) *ast.Node { - if isPrivateMethodTypeParameter(tx.host, input) && (input.DefaultType != nil || input.Constraint != nil) { - return tx.Factory().UpdateTypeParameterDeclaration( - input, - input.Modifiers(), - input.Name(), - nil, - nil, - ) - } - return tx.Visitor().VisitEachChild(input.AsNode()) -} - -func (tx *DeclarationTransformer) transformVariableDeclaration(input *ast.VariableDeclaration) *ast.Node { - if ast.IsBindingPattern(input.Name()) { - return tx.recreateBindingPattern(input.Name().AsBindingPattern()) - } - // Variable declaration types also suppress new diagnostic contexts, provided the contexts wouldn't be made for binding pattern types - tx.suppressNewDiagnosticContexts = true - return tx.Factory().UpdateVariableDeclaration( - input, - input.Name(), - nil, - tx.ensureType(input.AsNode(), false), - tx.ensureNoInitializer(input.AsNode()), - ) -} - -func (tx *DeclarationTransformer) recreateBindingPattern(input *ast.BindingPattern) *ast.Node { - var results []*ast.Node - for _, elem := range input.Elements.Nodes { - result := tx.recreateBindingElement(elem.AsBindingElement()) - if result == nil { - continue - } - if result.Kind == ast.KindSyntaxList { - results = append(results, result.AsSyntaxList().Children...) - } else { - results = append(results, result) - } - } - if len(results) == 0 { - return nil - } - if len(results) == 1 { - return results[0] - } - return tx.Factory().NewSyntaxList(results) -} - -func (tx *DeclarationTransformer) recreateBindingElement(e *ast.BindingElement) *ast.Node { - if e.Name() == nil { - return nil - } - if !getBindingNameVisible(tx.resolver, e.AsNode()) { - return nil - } - if ast.IsBindingPattern(e.Name()) { - return tx.recreateBindingPattern(e.Name().AsBindingPattern()) - } - return tx.Factory().NewVariableDeclaration( - e.Name(), - nil, - tx.ensureType(e.AsNode(), false), - nil, // TODO: possible strada bug - not emitting const initialized binding pattern elements? - ) -} - -func (tx *DeclarationTransformer) transformIndexSignatureDeclaration(input *ast.IndexSignatureDeclaration) *ast.Node { - t := tx.Visitor().Visit(input.Type) - if t == nil { - t = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) - } - return tx.Factory().UpdateIndexSignatureDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - tx.updateParamList(input.AsNode(), input.Parameters), - t, - ) -} - -func (tx *DeclarationTransformer) transformCallSignatureDeclaration(input *ast.CallSignatureDeclaration) *ast.Node { - return tx.Factory().UpdateCallSignatureDeclaration( - input, - tx.ensureTypeParams(input.AsNode(), input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.ensureType(input.AsNode(), false), - ) -} - -func (tx *DeclarationTransformer) transformPropertySignatureDeclaration(input *ast.PropertySignatureDeclaration) *ast.Node { - if ast.IsPrivateIdentifier(input.Name()) { - return nil - } - return tx.Factory().UpdatePropertySignatureDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - input.PostfixToken, - tx.ensureType(input.AsNode(), false), - tx.ensureNoInitializer(input.AsNode()), // TODO: possible strada bug (fixed here) - const property signatures never initialized - ) -} - -func (tx *DeclarationTransformer) transformPropertyDeclaration(input *ast.PropertyDeclaration) *ast.Node { - if ast.IsPrivateIdentifier(input.Name()) { - return nil - } - // Remove definite assignment assertion (!) from declaration files - postfixToken := input.PostfixToken - if postfixToken != nil && postfixToken.Kind == ast.KindExclamationToken { - postfixToken = nil - } - return tx.Factory().UpdatePropertyDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - postfixToken, - tx.ensureType(input.AsNode(), false), - tx.ensureNoInitializer(input.AsNode()), - ) -} - -func (tx *DeclarationTransformer) transformSetAccessorDeclaration(input *ast.SetAccessorDeclaration) *ast.Node { - if ast.IsPrivateIdentifier(input.Name()) { - return nil - } - - return tx.Factory().UpdateSetAccessorDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - nil, // accessors shouldn't have type params - tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), - nil, - nil, - nil, - ) -} - -func (tx *DeclarationTransformer) transformGetAccesorDeclaration(input *ast.GetAccessorDeclaration) *ast.Node { - if ast.IsPrivateIdentifier(input.Name()) { - return nil - } - return tx.Factory().UpdateGetAccessorDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - nil, // accessors shouldn't have type params - tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), - tx.ensureType(input.AsNode(), false), - nil, - nil, - ) -} - -func (tx *DeclarationTransformer) updateAccessorParamList(input *ast.Node, isPrivate bool) *ast.ParameterList { - var newParams []*ast.Node - if !isPrivate { - thisParam := ast.GetThisParameter(input) - if thisParam != nil { - newParams = append(newParams, tx.ensureParameter(thisParam.AsParameterDeclaration())) - } - } - if ast.IsSetAccessorDeclaration(input) { - var valueParam *ast.Node - if !isPrivate { - if len(newParams) == 1 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 2 { - valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[1].AsParameterDeclaration()) - } else if len(newParams) == 0 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 1 { - valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[0].AsParameterDeclaration()) - } - } - if valueParam == nil { - // TODO: strada bug - no type printed on set accessor missing arg as though private - var t *ast.Node - if !isPrivate { - t = tx.Factory().NewKeywordExpression(ast.KindAnyKeyword) - } - valueParam = tx.Factory().NewParameterDeclaration( - nil, - nil, - tx.Factory().NewIdentifier("value"), - nil, - t, - nil, - ) - } - newParams = append(newParams, valueParam) - } - return tx.Factory().NewNodeList(newParams) -} - -func (tx *DeclarationTransformer) transformConstructorDeclaration(input *ast.ConstructorDeclaration) *ast.Node { - // A constructor declaration may not have a type annotation - return tx.Factory().UpdateConstructorDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - nil, // no type params - tx.updateParamList(input.AsNode(), input.Parameters), - nil, // no return type - nil, - nil, - ) -} - -func (tx *DeclarationTransformer) transformConstructSignatureDeclaration(input *ast.ConstructSignatureDeclaration) *ast.Node { - return tx.Factory().UpdateConstructSignatureDeclaration( - input, - tx.ensureTypeParams(input.AsNode(), input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.ensureType(input.AsNode(), false), - ) -} - -func (tx *DeclarationTransformer) omitPrivateMethodType(input *ast.Node) *ast.Node { - if input.Symbol() != nil && len(input.Symbol().Declarations) > 0 && input.Symbol().Declarations[0] != input { - return nil - } else { - return tx.Factory().NewPropertyDeclaration( - tx.ensureModifiers(input), - input.Name(), - nil, - nil, - nil, - ) - } -} - -func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast.MethodSignatureDeclaration) *ast.Node { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { - return tx.omitPrivateMethodType(input.AsNode()) - } else if ast.IsPrivateIdentifier(input.Name()) { - return nil - } else { - return tx.Factory().UpdateMethodSignatureDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - input.PostfixToken, - tx.ensureTypeParams(input.AsNode(), input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.ensureType(input.AsNode(), false), - ) - } -} - -func (tx *DeclarationTransformer) transformMethodDeclaration(input *ast.MethodDeclaration) *ast.Node { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { - return tx.omitPrivateMethodType(input.AsNode()) - } else if ast.IsPrivateIdentifier(input.Name()) { - return nil - } else { - return tx.Factory().UpdateMethodDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - nil, - input.Name(), - input.PostfixToken, - tx.ensureTypeParams(input.AsNode(), input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.ensureType(input.AsNode(), false), - nil, - nil, - ) - } -} - -func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *ast.Node { - // !!! TODO: stripInternal support? - // if (shouldStripInternal(input)) return nil - switch input.Kind { - case ast.KindExportDeclaration: - if ast.IsSourceFile(input.Parent) { - tx.resultHasExternalModuleIndicator = true - } - tx.resultHasScopeMarker = true - // Rewrite external module names if necessary - return tx.Factory().UpdateExportDeclaration( - input.AsExportDeclaration(), - input.Modifiers(), - input.IsTypeOnly(), - input.AsExportDeclaration().ExportClause, - tx.rewriteModuleSpecifier(input, input.AsExportDeclaration().ModuleSpecifier), - tx.tryGetResolutionModeOverride(input.AsExportDeclaration().Attributes), - ) - case ast.KindExportAssignment, ast.KindJSExportAssignment: - if ast.IsSourceFile(input.Parent) { - tx.resultHasExternalModuleIndicator = true - } - tx.resultHasScopeMarker = true - if input.AsExportAssignment().Expression.Kind == ast.KindIdentifier { - return input - } - // expression is non-identifier, create _default typed variable to reference - newId := tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) - tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { - return &SymbolAccessibilityDiagnostic{ - diagnosticMessage: diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, - errorNode: input, - } - } - tx.tracker.PushErrorFallbackNode(input) - type_ := tx.ensureType(input, false) - varDecl := tx.Factory().NewVariableDeclaration(newId, nil, type_, nil) - tx.tracker.PopErrorFallbackNode() - var modList *ast.ModifierList - if tx.needsDeclare { - modList = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)}) - } else { - modList = tx.Factory().NewModifierList([]*ast.Node{}) - } - statement := tx.Factory().NewVariableStatement(modList, tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl}))) - - assignment := tx.Factory().UpdateExportAssignment(input.AsExportAssignment(), input.Modifiers(), input.Type(), newId) - // Remove comments from the export declaration and copy them onto the synthetic _default declaration - tx.preserveJsDoc(statement, input) - tx.removeAllComments(assignment) - return tx.Factory().NewSyntaxList([]*ast.Node{statement, assignment}) - default: - id := ast.GetNodeId(tx.EmitContext().MostOriginal(input)) - if tx.lateStatementReplacementMap[id] == nil { - // Don't actually transform yet; just leave as original node - will be elided/swapped by late pass - tx.lateStatementReplacementMap[id] = tx.transformTopLevelDeclaration(input) - } - return input - } -} - -func (tx *DeclarationTransformer) rewriteModuleSpecifier(parent *ast.Node, input *ast.Node) *ast.Node { - if input == nil { - return nil - } - tx.resultHasExternalModuleIndicator = tx.resultHasExternalModuleIndicator || (parent.Kind != ast.KindModuleDeclaration && parent.Kind != ast.KindImportType) - if ast.IsStringLiteralLike(input) { - if tx.isBundledEmit { - // !!! TODO: support bundled emit specifier rewriting - } - } - return input -} - -func (tx *DeclarationTransformer) tryGetResolutionModeOverride(node *ast.Node) *ast.Node { - if node == nil { - return node - } - mode := tx.host.GetResolutionModeOverride(node) - if mode != core.ResolutionModeNone { - return node - } - return nil -} - -func (tx *DeclarationTransformer) preserveJsDoc(updated *ast.Node, original *ast.Node) { - // !!! TODO: JSDoc comment support - // if (hasJSDocNodes(updated) && hasJSDocNodes(original)) { - // updated.jsDoc = original.jsDoc; - // } - // return setCommentRange(updated, getCommentRange(original)); -} - -func (tx *DeclarationTransformer) removeAllComments(node *ast.Node) { - tx.EmitContext().AddEmitFlags(node, printer.EFNoComments) - // !!! TODO: Also remove synthetic trailing/leading comments added by transforms - // emitNode.leadingComments = undefined; - // emitNode.trailingComments = undefined; -} - -func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) *ast.Node { - if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { - // Private nodes emit no types (except private parameter properties, whose parameter types are actually visible) - return nil - } - - if tx.shouldPrintWithInitializer(node) { - // Literal const declarations will have an initializer ensured rather than a type - return nil - } - - // Should be removed createTypeOfDeclaration will actually now reuse the existing annotation so there is no real need to duplicate type walking - // Left in for now to minimize diff during syntactic type node builder refactor - if !ast.IsExportAssignment(node) && !ast.IsBindingElement(node) && node.Type() != nil && (!ast.IsParameter(node) || !tx.resolver.RequiresAddingImplicitUndefined(node, nil, tx.enclosingDeclaration)) { - return tx.Visitor().Visit(node.Type()) - } - - oldErrorNameNode := tx.state.errorNameNode - tx.state.errorNameNode = node.Name() - var oldDiag GetSymbolAccessibilityDiagnostic - if !tx.suppressNewDiagnosticContexts { - oldDiag = tx.state.getSymbolAccessibilityDiagnostic - if canProduceDiagnostics(node) { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node) - } - } - var typeNode *ast.Node - - if hasInferredType(node) { - typeNode = tx.resolver.CreateTypeOfDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) - } else if ast.IsFunctionLike(node) { - typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) - } else { - debug.AssertNever(node) - } - - tx.state.errorNameNode = oldErrorNameNode - if !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - } - if typeNode == nil { - return tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) - } - return typeNode -} - -func (tx *DeclarationTransformer) shouldPrintWithInitializer(node *ast.Node) bool { - return canHaveLiteralInitializer(tx.host, node) && node.Initializer() != nil && tx.resolver.IsLiteralConstDeclaration(tx.EmitContext().MostOriginal(node)) -} - -func (tx *DeclarationTransformer) checkEntityNameVisibility(entityName *ast.Node, enclosingDeclaration *ast.Node) { - visibilityResult := tx.resolver.IsEntityNameVisible(entityName, enclosingDeclaration) - tx.tracker.handleSymbolAccessibilityError(visibilityResult) -} - -// Transforms the direct child of a source file into zero or more replacement statements -func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) *ast.Node { - if len(tx.state.lateMarkedStatements) > 0 { - // Remove duplicates of the current statement from the deferred work queue (this was done via orderedRemoveItem in strada - why? to ensure the same backing array? microop?) - tx.state.lateMarkedStatements = core.Filter(tx.state.lateMarkedStatements, func(node *ast.Node) bool { return node != input }) - } - // !!! TODO: stripInternal support? - // if (shouldStripInternal(input)) return; - if input.Kind == ast.KindImportEqualsDeclaration { - return tx.transformImportEqualsDeclaration(input.AsImportEqualsDeclaration()) - } - if input.Kind == ast.KindImportDeclaration || input.Kind == ast.KindJSImportDeclaration { - res := tx.transformImportDeclaration(input.AsImportDeclaration()) - if res != nil && res.Kind != ast.KindImportDeclaration { - res := res.Clone(tx.Factory()) - res.Kind = ast.KindImportDeclaration - return res - } - return res - } - if ast.IsDeclaration(input) && isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) { - return nil - } - - // !!! TODO: JSDoc support - // if (isJSDocImportTag(input)) return; - - // Elide implementation signatures from overload sets - if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) { - return nil - } - previousEnclosingDeclaration := tx.enclosingDeclaration - if isEnclosingDeclaration(input) { - tx.enclosingDeclaration = input - } - - canProdiceDiagnostic := canProduceDiagnostics(input) - oldDiag := tx.state.getSymbolAccessibilityDiagnostic - oldName := tx.state.errorNameNode - if canProdiceDiagnostic { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) - } - saveNeedsDeclare := tx.needsDeclare - - var result *ast.Node - switch input.Kind { - case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: - result = tx.transformTypeAliasDeclaration(input.AsTypeAliasDeclaration()) - case ast.KindInterfaceDeclaration: - result = tx.transformInterfaceDeclaration(input.AsInterfaceDeclaration()) - case ast.KindFunctionDeclaration: - result = tx.transformFunctionDeclaration(input.AsFunctionDeclaration()) - case ast.KindModuleDeclaration: - result = tx.transformModuleDeclaration(input.AsModuleDeclaration()) - case ast.KindClassDeclaration: - result = tx.transformClassDeclaration(input.AsClassDeclaration()) - case ast.KindVariableStatement: - result = tx.transformVariableStatement(input.AsVariableStatement()) - case ast.KindEnumDeclaration: - result = tx.transformEnumDeclaration(input.AsEnumDeclaration()) - default: - // Anything left unhandled is an error, so this should be unreachable - panic(fmt.Sprintf("Unhandled top-level node in declaration emit: %q", input.Kind)) - } - - tx.enclosingDeclaration = previousEnclosingDeclaration - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - tx.needsDeclare = saveNeedsDeclare - tx.state.errorNameNode = oldName - return result -} - -func (tx *DeclarationTransformer) transformTypeAliasDeclaration(input *ast.TypeAliasDeclaration) *ast.Node { - tx.needsDeclare = false - return tx.Factory().UpdateTypeAliasDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - tx.Visitor().VisitNodes(input.TypeParameters), - tx.Visitor().Visit(input.Type), - ) -} - -func (tx *DeclarationTransformer) transformInterfaceDeclaration(input *ast.InterfaceDeclaration) *ast.Node { - return tx.Factory().UpdateInterfaceDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - tx.Visitor().VisitNodes(input.TypeParameters), - tx.Visitor().VisitNodes(input.HeritageClauses), - tx.Visitor().VisitNodes(input.Members), - ) -} - -func (tx *DeclarationTransformer) transformFunctionDeclaration(input *ast.FunctionDeclaration) *ast.Node { - return tx.Factory().UpdateFunctionDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - nil, - input.Name(), - tx.ensureTypeParams(input.AsNode(), input.TypeParameters), - tx.updateParamList(input.AsNode(), input.Parameters), - tx.ensureType(input.AsNode(), false), - nil, /*fullSignature*/ - nil, - ) -} - -func (tx *DeclarationTransformer) transformModuleDeclaration(input *ast.ModuleDeclaration) *ast.Node { - // !!! TODO: module declarations are now parsed into nested module objects with export modifiers - // It'd be good to collapse those back in the declaration output, but the AST can't represent the - // `namespace a.b.c` shape for the printer (without using invalid identifier names). - mods := tx.ensureModifiers(input.AsNode()) - saveNeedsDeclare := tx.needsDeclare - tx.needsDeclare = false - inner := input.Body - keyword := input.Keyword - if keyword != ast.KindGlobalKeyword && (input.Name() == nil || !ast.IsStringLiteral(input.Name())) { - keyword = ast.KindNamespaceKeyword - } - - if inner != nil && inner.Kind == ast.KindModuleBlock { - oldNeedsScopeFix := tx.needsScopeFixMarker - oldHasScopeFix := tx.resultHasScopeMarker - tx.resultHasScopeMarker = false - tx.needsScopeFixMarker = false - statements := tx.Visitor().VisitNodes(inner.AsModuleBlock().Statements) - lateStatements := tx.transformAndReplaceLatePaintedStatements(statements) - if input.Flags&ast.NodeFlagsAmbient != 0 { - tx.needsScopeFixMarker = false // If it was `declare`'d everything is implicitly exported already, ignore late printed "privates" - } - // With the final list of statements, there are 3 possibilities: - // 1. There's an export assignment or export declaration in the namespace - do nothing - // 2. Everything is exported and there are no export assignments or export declarations - strip all export modifiers - // 3. Some things are exported, some are not, and there's no marker - add an empty marker - if !ast.IsGlobalScopeAugmentation(input.AsNode()) && !tx.resultHasScopeMarker && !hasScopeMarker(lateStatements) { - if tx.needsScopeFixMarker { - lateStatements = tx.Factory().NewNodeList(append(lateStatements.Nodes, createEmptyExports(tx.Factory().AsNodeFactory()))) - } else { - lateStatements = tx.EmitContext().NewNodeVisitor(tx.stripExportModifiers).VisitNodes(lateStatements) - } - } - - body := tx.Factory().UpdateModuleBlock(inner.AsModuleBlock(), lateStatements) - tx.needsDeclare = saveNeedsDeclare - tx.needsScopeFixMarker = oldNeedsScopeFix - tx.resultHasScopeMarker = oldHasScopeFix - - return tx.Factory().UpdateModuleDeclaration( - input, - mods, - keyword, - input.Name(), - body, - ) - } - if inner != nil { - // trigger visit. ignore result (is deferred, so is just inner unless elided) - tx.Visitor().Visit(inner) - // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) - original := tx.EmitContext().MostOriginal(inner) - id := ast.GetNodeId(original) - body, _ := tx.lateStatementReplacementMap[id] - delete(tx.lateStatementReplacementMap, id) - return tx.Factory().UpdateModuleDeclaration( - input, - mods, - keyword, - input.Name(), - body, - ) - } - return tx.Factory().UpdateModuleDeclaration( - input, - mods, - keyword, - input.Name(), - nil, - ) -} - -func (tx *DeclarationTransformer) stripExportModifiers(statement *ast.Node) *ast.Node { - if statement == nil { - return nil - } - if ast.IsImportEqualsDeclaration(statement) || tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(statement), ast.ModifierFlagsDefault) != 0 || !ast.CanHaveModifiers(statement) { - // `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace - // Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too - return statement - } - - oldFlags := ast.GetCombinedModifierFlags(statement) - if oldFlags&ast.ModifierFlagsExport == 0 { - return statement - } - newFlags := oldFlags & (ast.ModifierFlagsAll ^ ast.ModifierFlagsExport) - modifiers := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) - return ast.ReplaceModifiers(tx.Factory().AsNodeFactory(), statement, tx.Factory().NewModifierList(modifiers)) -} - -func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDeclaration) *ast.Node { - tx.state.errorNameNode = input.Name() - tx.tracker.PushErrorFallbackNode(input.AsNode()) - defer tx.tracker.PopErrorFallbackNode() - - modifiers := tx.ensureModifiers(input.AsNode()) - typeParameters := tx.ensureTypeParams(input.AsNode(), input.TypeParameters) - ctor := getFirstConstructorWithBody(input.AsNode()) - var parameterProperties []*ast.Node - if ctor != nil { - oldDiag := tx.state.getSymbolAccessibilityDiagnostic - for _, param := range ctor.AsConstructorDeclaration().Parameters.Nodes { - if !ast.HasSyntacticModifier(param, ast.ModifierFlagsParameterPropertyModifier) { - continue - } - // !!! TODO: stripInternal support? - // if (shouldStripInternal(param)) { continue } - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param) - if param.Name().Kind == ast.KindIdentifier { - updated := tx.Factory().NewPropertyDeclaration( - tx.ensureModifiers(param), - param.Name(), - param.AsParameterDeclaration().QuestionToken, - tx.ensureType(param, false), - tx.ensureNoInitializer(param), - ) - tx.preserveJsDoc(updated, param) - parameterProperties = append(parameterProperties, updated) - } else { - // Pattern - this is currently an error, but we emit declarations for it somewhat correctly - // !!! is this worth reimplementing? We never made it not-an-error - } - } - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - } - - // When the class has at least one private identifier, create a unique constant identifier to retain the nominal typing behavior - // Prevents other classes with the same public members from being used in place of the current class - var privateIdentifier *ast.Node - if core.Some(input.Members.Nodes, func(member *ast.Node) bool { return member.Name() != nil && ast.IsPrivateIdentifier(member.Name()) }) { - privateIdentifier = tx.Factory().NewPropertyDeclaration( - nil, - tx.Factory().NewPrivateIdentifier("#private"), - nil, - nil, - nil, - ) - } - - lateIndexes := tx.resolver.CreateLateBoundIndexSignatures( - tx.EmitContext(), - input.AsNode(), - tx.enclosingDeclaration, - declarationEmitNodeBuilderFlags, - declarationEmitInternalNodeBuilderFlags, - tx.tracker, - ) - - memberNodes := make([]*ast.Node, 0, len(input.Members.Nodes)) - if privateIdentifier != nil { - memberNodes = append(memberNodes, privateIdentifier) - } - memberNodes = append(memberNodes, lateIndexes...) - memberNodes = append(memberNodes, parameterProperties...) - visitResult := tx.Visitor().VisitNodes(input.Members) - if visitResult != nil && len(visitResult.Nodes) > 0 { - memberNodes = append(memberNodes, visitResult.Nodes...) - } - members := tx.Factory().NewNodeList(memberNodes) - - extendsClause := getEffectiveBaseTypeNode(input.AsNode()) - - if extendsClause != nil && !ast.IsEntityNameExpression(extendsClause.AsExpressionWithTypeArguments().Expression) && extendsClause.AsExpressionWithTypeArguments().Expression.Kind != ast.KindNullKeyword { - oldId := "default" - if ast.NodeIsPresent(input.Name()) && ast.IsIdentifier(input.Name()) && len(input.Name().AsIdentifier().Text) > 0 { - oldId = input.Name().AsIdentifier().Text - } - newId := tx.Factory().NewUniqueNameEx(oldId+"_base", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) - tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { - return &SymbolAccessibilityDiagnostic{ - diagnosticMessage: diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1, - errorNode: extendsClause, - typeName: input.Name(), - } - } - - varDecl := tx.Factory().NewVariableDeclaration( - newId, - nil, - tx.resolver.CreateTypeOfExpression(tx.EmitContext(), extendsClause.Expression(), input.AsNode(), declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker), - nil, - ) - var mods *ast.ModifierList - if tx.needsDeclare { - mods = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)}) - } - statement := tx.Factory().NewVariableStatement( - mods, - tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl})), - ) - newHeritageClause := tx.Factory().UpdateHeritageClause( - extendsClause.Parent.AsHeritageClause(), - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().UpdateExpressionWithTypeArguments( - extendsClause.AsExpressionWithTypeArguments(), - newId, - tx.Visitor().VisitNodes(extendsClause.AsExpressionWithTypeArguments().TypeArguments), - ), - }), - ) - retainedHeritageClauses := tx.Visitor().VisitNodes(input.HeritageClauses) // should just be `implements` - heritageList := []*ast.Node{ - newHeritageClause, - } - if retainedHeritageClauses != nil && len(retainedHeritageClauses.Nodes) > 0 { - heritageList = append(heritageList, retainedHeritageClauses.Nodes...) - } - heritageClauses := tx.Factory().NewNodeList(heritageList) - - return tx.Factory().NewSyntaxList([]*ast.Node{ - statement, - tx.Factory().UpdateClassDeclaration( - input, - modifiers, - input.Name(), - typeParameters, - heritageClauses, - members, - ), - }) - } - - return tx.Factory().UpdateClassDeclaration( - input, - modifiers, - input.Name(), - typeParameters, - tx.Visitor().VisitNodes(input.HeritageClauses), - members, - ) -} - -func (tx *DeclarationTransformer) transformVariableStatement(input *ast.VariableStatement) *ast.Node { - visible := false - for _, decl := range input.DeclarationList.AsVariableDeclarationList().Declarations.Nodes { - visible = getBindingNameVisible(tx.resolver, decl) - if visible { - break - } - } - if !visible { - return nil - } - - nodes := tx.Visitor().VisitNodes(input.DeclarationList.AsVariableDeclarationList().Declarations) - if nodes != nil && len(nodes.Nodes) == 0 { - return nil - } - - modifiers := tx.ensureModifiers(input.AsNode()) - - var declList *ast.Node - if ast.IsVarUsing(input.DeclarationList) || ast.IsVarAwaitUsing(input.DeclarationList) { - declList = tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, nodes) - tx.EmitContext().SetOriginal(declList, input.DeclarationList) - tx.EmitContext().SetCommentRange(declList, input.DeclarationList.Loc) - declList.Loc = input.DeclarationList.Loc - } else { - declList = tx.Factory().UpdateVariableDeclarationList(input.DeclarationList.AsVariableDeclarationList(), nodes) - } - return tx.Factory().UpdateVariableStatement(input, modifiers, declList) -} - -func (tx *DeclarationTransformer) transformEnumDeclaration(input *ast.EnumDeclaration) *ast.Node { - return tx.Factory().UpdateEnumDeclaration( - input, - tx.ensureModifiers(input.AsNode()), - input.Name(), - tx.Factory().NewNodeList(core.MapNonNil(input.Members.Nodes, func(m *ast.Node) *ast.Node { - // !!! TODO: stripInternal support? - // if (shouldStripInternal(m)) return; - - // !!! TODO: isolatedDeclarations support - // if ( - // isolatedDeclarations && m.initializer && enumValue?.hasExternalReferences && - // // This will be its own compiler error instead, so don't report. - // !isComputedPropertyName(m.name) - // ) { - // context.addDiagnostic(createDiagnosticForNode(m, Diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations)); - // } - - // Rewrite enum values to their constants, if available - enumValue := tx.resolver.GetEnumMemberValue(m) - var newInitializer *ast.Node - switch value := enumValue.Value.(type) { - case jsnum.Number: - if value >= 0 { - newInitializer = tx.Factory().NewNumericLiteral(value.String()) - } else { - newInitializer = tx.Factory().NewPrefixUnaryExpression( - ast.KindMinusToken, - tx.Factory().NewNumericLiteral((-value).String()), - ) - } - case string: - newInitializer = tx.Factory().NewStringLiteral(value) - default: - // nil - newInitializer = nil - } - result := tx.Factory().UpdateEnumMember(m.AsEnumMember(), m.Name(), newInitializer) - tx.preserveJsDoc(result, m) - return result - })), - ) -} - -func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) *ast.ModifierList { - currentFlags := tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsAll) - newFlags := tx.ensureModifierFlags(node) - if currentFlags == newFlags { - // Elide decorators - mods := node.Modifiers() - if mods == nil { - return mods - } - return tx.Factory().NewModifierList(core.Filter(mods.Nodes, ast.IsModifier)) - } - result := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) - if len(result) == 0 { - return nil - } - return tx.Factory().NewModifierList(result) -} - -func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.ModifierFlags { - mask := ast.ModifierFlagsAll ^ (ast.ModifierFlagsPublic | ast.ModifierFlagsAsync | ast.ModifierFlagsOverride) // No async and override modifiers in declaration files - additions := ast.ModifierFlagsNone - if tx.needsDeclare && !isAlwaysType(node) { - additions = ast.ModifierFlagsAmbient - } - parentIsFile := node.Parent.Kind == ast.KindSourceFile - if !parentIsFile || (tx.isBundledEmit && parentIsFile && ast.IsExternalModule(node.Parent.AsSourceFile())) { - mask ^= ast.ModifierFlagsAmbient - additions = ast.ModifierFlagsNone - } - return maskModifierFlags(tx.host, node, mask, additions) -} - -func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.TypeParameterList) *ast.TypeParameterList { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { - return nil - } - var typeParameters *ast.TypeParameterList - if typeParameters = tx.Visitor().VisitNodes(params); typeParameters != nil { - return typeParameters - } - oldErrorNameNode := tx.state.errorNameNode - tx.state.errorNameNode = node.Name() - var oldDiag GetSymbolAccessibilityDiagnostic - if !tx.suppressNewDiagnosticContexts { - oldDiag = tx.state.getSymbolAccessibilityDiagnostic - if canProduceDiagnostics(node) { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node) - } - } - - if data := node.FunctionLikeData(); data != nil && data.FullSignature != nil { - if nodes := tx.resolver.CreateTypeParametersOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker); nodes != nil { - typeParameters = &ast.TypeParameterList{ - Loc: node.Loc, - Nodes: nodes, - } - } - } - - tx.state.errorNameNode = oldErrorNameNode - if !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - } - return typeParameters -} - -func (tx *DeclarationTransformer) updateParamList(node *ast.Node, params *ast.ParameterList) *ast.ParameterList { - if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 { - return tx.Factory().NewNodeList([]*ast.Node{}) - } - results := make([]*ast.Node, len(params.Nodes)) - for i, p := range params.Nodes { - results[i] = tx.ensureParameter(p.AsParameterDeclaration()) - } - return tx.Factory().NewNodeList(results) -} - -func (tx *DeclarationTransformer) ensureParameter(p *ast.ParameterDeclaration) *ast.Node { - oldDiag := tx.state.getSymbolAccessibilityDiagnostic - if !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.AsNode()) - } - var questionToken *ast.TokenNode - if tx.resolver.IsOptionalParameter(p.AsNode()) { - if p.QuestionToken != nil { - questionToken = p.QuestionToken - } else { - questionToken = tx.Factory().NewToken(ast.KindQuestionToken) - } - } - result := tx.Factory().UpdateParameterDeclaration( - p, - nil, - p.DotDotDotToken, - tx.filterBindingPatternInitializers(p.Name()), - questionToken, - tx.ensureType(p.AsNode(), true), - tx.ensureNoInitializer(p.AsNode()), - ) - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - return result -} - -func (tx *DeclarationTransformer) ensureNoInitializer(node *ast.Node) *ast.Node { - if tx.shouldPrintWithInitializer(node) { - unwrappedInitializer := unwrapParenthesizedExpression(node.Initializer()) - if !isPrimitiveLiteralValue(unwrappedInitializer, true) { - tx.tracker.ReportInferenceFallback(node) - } - return tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(node), tx.tracker) - } - return nil -} - -func (tx *DeclarationTransformer) filterBindingPatternInitializers(node *ast.Node) *ast.Node { - if node.Kind == ast.KindIdentifier { - return node - } else { - // TODO: visitor to avoid always making new nodes? - elements := make([]*ast.Node, 0, len(node.AsBindingPattern().Elements.Nodes)) - for _, elem := range node.AsBindingPattern().Elements.Nodes { - if elem.Kind == ast.KindOmittedExpression { - elements = append(elements, elem) - continue - } - if elem.PropertyName() != nil && ast.IsComputedPropertyName(elem.PropertyName()) && ast.IsEntityNameExpression(elem.PropertyName().Expression()) { - tx.checkEntityNameVisibility(elem.PropertyName().Expression(), tx.enclosingDeclaration) - } - if elem.Name() == nil { - elements = append(elements, elem) - continue - } - - elements = append(elements, tx.Factory().UpdateBindingElement( - elem.AsBindingElement(), - elem.AsBindingElement().DotDotDotToken, - elem.PropertyName(), - tx.filterBindingPatternInitializers(elem.Name()), - nil, - )) - } - elemList := tx.Factory().NewNodeList(elements) - return tx.Factory().UpdateBindingPattern(node.AsBindingPattern(), elemList) - } -} - -func (tx *DeclarationTransformer) transformImportEqualsDeclaration(decl *ast.ImportEqualsDeclaration) *ast.Node { - if !tx.resolver.IsDeclarationVisible(decl.AsNode()) { - return nil - } - if decl.ModuleReference.Kind == ast.KindExternalModuleReference { - // Rewrite external module names if necessary - specifier := ast.GetExternalModuleImportEqualsDeclarationExpression(decl.AsNode()) - return tx.Factory().UpdateImportEqualsDeclaration( - decl, - decl.Modifiers(), - decl.IsTypeOnly, - decl.Name(), - tx.Factory().UpdateExternalModuleReference(decl.ModuleReference.AsExternalModuleReference(), tx.rewriteModuleSpecifier(decl.AsNode(), specifier)), - ) - } else { - oldDiag := tx.state.getSymbolAccessibilityDiagnostic - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(decl.AsNode()) - tx.checkEntityNameVisibility(decl.ModuleReference, tx.enclosingDeclaration) - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - return decl.AsNode() - } -} - -func (tx *DeclarationTransformer) transformImportDeclaration(decl *ast.ImportDeclaration) *ast.Node { - if decl.ImportClause == nil { - // import "mod" - possibly needed for side effects? (global interface patches, module augmentations, etc) - return tx.Factory().UpdateImportDeclaration( - decl, - decl.Modifiers(), - decl.ImportClause, - tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), - tx.tryGetResolutionModeOverride(decl.Attributes), - ) - } - phaseModifier := decl.ImportClause.AsImportClause().PhaseModifier - if phaseModifier == ast.KindDeferKeyword { - phaseModifier = ast.KindUnknown - } - // The `importClause` visibility corresponds to the default's visibility. - var visibleDefaultBinding *ast.Node - if decl.ImportClause != nil && decl.ImportClause.Name() != nil && tx.resolver.IsDeclarationVisible(decl.ImportClause) { - visibleDefaultBinding = decl.ImportClause.Name() - } - if decl.ImportClause.AsImportClause().NamedBindings == nil { - // No named bindings (either namespace or list), meaning the import is just default or should be elided - if visibleDefaultBinding == nil { - return nil - } - return tx.Factory().UpdateImportDeclaration( - decl, - decl.Modifiers(), - tx.Factory().UpdateImportClause( - decl.ImportClause.AsImportClause(), - phaseModifier, - visibleDefaultBinding, - /*namedBindings*/ nil, - ), - tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), - tx.tryGetResolutionModeOverride(decl.Attributes), - ) - } - if decl.ImportClause.AsImportClause().NamedBindings.Kind == ast.KindNamespaceImport { - // Namespace import (optionally with visible default) - var namedBindings *ast.Node - if tx.resolver.IsDeclarationVisible(decl.ImportClause.AsImportClause().NamedBindings) { - namedBindings = decl.ImportClause.AsImportClause().NamedBindings - } - if visibleDefaultBinding == nil && namedBindings == nil { - return nil - } - return tx.Factory().UpdateImportDeclaration( - decl, - decl.Modifiers(), - tx.Factory().UpdateImportClause( - decl.ImportClause.AsImportClause(), - phaseModifier, - visibleDefaultBinding, - namedBindings, - ), - tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), - tx.tryGetResolutionModeOverride(decl.Attributes), - ) - } - // Named imports (optionally with visible default) - bindingList := core.Filter( - decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports().Elements.Nodes, - func(b *ast.Node) bool { - return tx.resolver.IsDeclarationVisible(b) - }, - ) - if len(bindingList) > 0 || visibleDefaultBinding != nil { - var namedImports *ast.Node - if len(bindingList) > 0 { - namedImports = tx.Factory().UpdateNamedImports( - decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports(), - tx.Factory().NewNodeList(bindingList), - ) - } - return tx.Factory().UpdateImportDeclaration( - decl, - decl.Modifiers(), - tx.Factory().UpdateImportClause( - decl.ImportClause.AsImportClause(), - phaseModifier, - visibleDefaultBinding, - namedImports, - ), - tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), - tx.tryGetResolutionModeOverride(decl.Attributes), - ) - } - // Augmentation of export depends on import - if tx.resolver.IsImportRequiredByAugmentation(decl) { - // IsolatedDeclarations support - // if (isolatedDeclarations) { - // context.addDiagnostic(createDiagnosticForNode(decl, Diagnostics.Declaration_emit_for_this_file_requires_preserving_this_import_for_augmentations_This_is_not_supported_with_isolatedDeclarations)); - // } - return tx.Factory().UpdateImportDeclaration( - decl, - decl.Modifiers(), - /*importClause*/ nil, - tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), - tx.tryGetResolutionModeOverride(decl.Attributes), - ) - } - // Nothing visible - return nil -} - -func (tx *DeclarationTransformer) transformJSDocTypeExpression(input *ast.JSDocTypeExpression) *ast.Node { - return tx.Visitor().Visit(input.Type) -} - -func (tx *DeclarationTransformer) transformJSDocTypeLiteral(input *ast.JSDocTypeLiteral) *ast.Node { - members, _ := tx.Visitor().VisitSlice(input.JSDocPropertyTags) - replacement := tx.Factory().NewTypeLiteralNode(tx.Factory().NewNodeList(members)) - tx.EmitContext().SetOriginal(replacement, input.AsNode()) - return replacement -} - -func (tx *DeclarationTransformer) transformJSDocPropertyTag(input *ast.JSDocPropertyTag) *ast.Node { - replacement := tx.Factory().NewPropertySignatureDeclaration( - nil, - tx.Visitor().Visit(input.TagName), - nil, - tx.Visitor().Visit(input.TypeExpression), - nil, - ) - tx.EmitContext().SetOriginal(replacement, input.AsNode()) - return replacement -} - -func (tx *DeclarationTransformer) transformJSDocAllType(input *ast.JSDocAllType) *ast.Node { - replacement := tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) - tx.EmitContext().SetOriginal(replacement, input.AsNode()) - return replacement -} - -func (tx *DeclarationTransformer) transformJSDocNullableType(input *ast.JSDocNullableType) *ast.Node { - replacement := tx.Factory().NewUnionTypeNode(tx.Factory().NewNodeList([]*ast.Node{ - tx.Visitor().Visit(input.Type), - tx.Factory().NewLiteralTypeNode(tx.Factory().NewKeywordExpression(ast.KindNullKeyword)), - })) - tx.EmitContext().SetOriginal(replacement, input.AsNode()) - return replacement -} - -func (tx *DeclarationTransformer) transformJSDocNonNullableType(input *ast.JSDocNonNullableType) *ast.Node { - return tx.Visitor().Visit(input.Type) -} - -func (tx *DeclarationTransformer) transformJSDocVariadicType(input *ast.JSDocVariadicType) *ast.Node { - replacement := tx.Factory().NewArrayTypeNode(tx.Visitor().Visit(input.Type)) - tx.EmitContext().SetOriginal(replacement, input.AsNode()) - return replacement -} - -func (tx *DeclarationTransformer) transformJSDocOptionalType(input *ast.JSDocOptionalType) *ast.Node { - replacement := tx.Factory().NewUnionTypeNode(tx.Factory().NewNodeList([]*ast.Node{ - tx.Visitor().Visit(input.Type), - tx.Factory().NewKeywordTypeNode(ast.KindUndefinedKeyword), - })) - tx.EmitContext().SetOriginal(replacement, input.AsNode()) - return replacement -} - -func (tx *DeclarationTransformer) visitExpressionStatement(node *ast.ExpressionStatement) *ast.Node { - expression := node.Expression - if expression == nil { - return nil - } - - if expression.Kind == ast.KindBinaryExpression && - ast.GetAssignmentDeclarationKind(expression.AsBinaryExpression()) == ast.JSDeclarationKindProperty { - return tx.transformExpandoAssignment(expression.AsBinaryExpression()) - } - - return nil -} - -func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExpression) *ast.Node { - left := node.Left - - symbol := node.Symbol - if symbol == nil || symbol.Flags&ast.SymbolFlagsAssignment == 0 { - return nil - } - - ns := ast.GetLeftmostAccessExpression(left) - if ns == nil || ns.Kind != ast.KindIdentifier { - return nil - } - - declaration := tx.resolver.GetReferencedValueDeclaration(ns) - if declaration == nil { - return nil - } - - host := declaration.Symbol() - if host == nil { - return nil - } - - name := tx.Factory().NewIdentifier(ns.Text()) - property := tx.tryGetPropertyName(left) - if property == "" || !scanner.IsIdentifierText(property, core.LanguageVariantStandard) { - return nil - } - - tx.transformExpandoHost(name, declaration) - - if ast.IsFunctionDeclaration(declaration) && !shouldEmitFunctionProperties(declaration.AsFunctionDeclaration()) { - return nil - } - - isNonContextualKeywordName := ast.IsNonContextualKeyword(scanner.StringToToken(property)) - exportName := core.IfElse(isNonContextualKeywordName, tx.Factory().NewGeneratedNameForNode(left), tx.Factory().NewIdentifier(property)) - - synthesizedNamespace := tx.Factory().NewModuleDeclaration(nil /*modifiers*/, ast.KindNamespaceKeyword, name, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList([]*ast.Node{}))) - synthesizedNamespace.Parent = tx.enclosingDeclaration - - declarationData := synthesizedNamespace.DeclarationData() - declarationData.Symbol = host - - containerData := synthesizedNamespace.LocalsContainerData() - containerData.Locals = make(ast.SymbolTable, 0) - - saveDiag := tx.state.getSymbolAccessibilityDiagnostic - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node.AsNode()) - t := tx.resolver.CreateTypeOfExpression(tx.EmitContext(), left, synthesizedNamespace, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags|nodebuilder.InternalFlagsNoSyntacticPrinter, tx.tracker) - tx.state.getSymbolAccessibilityDiagnostic = saveDiag - - statements := []*ast.Statement{ - tx.Factory().NewVariableStatement( - nil, /*modifiers*/ - tx.Factory().NewVariableDeclarationList( - ast.NodeFlagsNone, - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewVariableDeclaration(exportName, nil /*exclamationToken*/, t, nil /*initializer*/), - }), - ), - ), - } - - if isNonContextualKeywordName { - namedExports := tx.Factory().NewNamedExports(tx.Factory().NewNodeList( - []*ast.Node{ - tx.Factory().NewExportSpecifier(false /*isTypeOnly*/, exportName, tx.Factory().NewIdentifier(left.Name().Text())), - }, - )) - statements = append(statements, tx.Factory().NewExportDeclaration(nil /*modifiers*/, false /*isTypeOnly*/, namedExports, nil /*moduleSpecifier*/, nil /*attributes*/)) - } - - flags := tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(declaration), ast.ModifierFlagsAll) - modifierFlags := ast.ModifierFlagsAmbient - - if flags&ast.ModifierFlagsExport != 0 { - if flags&ast.ModifierFlagsDefault == 0 { - modifierFlags |= ast.ModifierFlagsExport - } - tx.resultHasScopeMarker = true - tx.resultHasExternalModuleIndicator = true - } - - return tx.Factory().NewModuleDeclaration(tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(modifierFlags, tx.Factory().NewModifier)), ast.KindNamespaceKeyword, name, tx.Factory().NewModuleBlock(tx.Factory().NewNodeList(statements))) -} - -func (tx *DeclarationTransformer) transformExpandoHost(name *ast.Node, declaration *ast.Declaration) { - root := core.IfElse(ast.IsVariableDeclaration(declaration), declaration.Parent.Parent, declaration) - id := ast.GetNodeId(tx.EmitContext().MostOriginal(root)) - - if tx.expandoHosts.Has(id) { - return - } - - saveNeedsDeclare := tx.needsDeclare - tx.needsDeclare = true - - modifierFlags := tx.ensureModifierFlags(root) - defaultExport := modifierFlags&ast.ModifierFlagsExport != 0 && modifierFlags&ast.ModifierFlagsDefault != 0 - - tx.needsDeclare = saveNeedsDeclare - - if defaultExport { - modifierFlags |= ast.ModifierFlagsAmbient - modifierFlags ^= ast.ModifierFlagsDefault - modifierFlags ^= ast.ModifierFlagsExport - } - - modifiers := tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(modifierFlags, tx.Factory().NewModifier)) - replacement := make([]*ast.Node, 0) - - if ast.IsFunctionDeclaration(declaration) { - typeParameters, parameters, asteriskToken := extractExpandoHostParams(declaration) - replacement = append(replacement, tx.Factory().UpdateFunctionDeclaration(declaration.AsFunctionDeclaration(), modifiers, asteriskToken, declaration.Name(), tx.ensureTypeParams(declaration, typeParameters), tx.updateParamList(declaration, parameters), tx.ensureType(declaration, false), nil /*fullSignature*/, nil /*body*/)) - } else if ast.IsVariableDeclaration(declaration) && ast.IsFunctionExpressionOrArrowFunction(declaration.Initializer()) { - fn := declaration.Initializer() - typeParameters, parameters, asteriskToken := extractExpandoHostParams(fn) - replacement = append(replacement, tx.Factory().NewFunctionDeclaration(modifiers, asteriskToken, tx.Factory().NewIdentifier(name.Text()), tx.ensureTypeParams(fn, typeParameters), tx.updateParamList(fn, parameters), tx.ensureType(fn, false), nil /*fullSignature*/, nil /*body*/)) - } else { - return - } - - if defaultExport { - replacement = append(replacement, tx.Factory().NewExportAssignment(nil /*modifiers*/, false /*isExportEquals*/, nil /*typeNode*/, name)) - } - - tx.expandoHosts.Add(id) - tx.lateStatementReplacementMap[id] = tx.Factory().NewSyntaxList(replacement) -} - -func extractExpandoHostParams(node *ast.Node) (typeParameters *ast.TypeParameterList, parameters *ast.ParameterList, asteriskToken *ast.TokenNode) { - switch node.Kind { - case ast.KindFunctionExpression: - fn := node.AsFunctionExpression() - return fn.TypeParameters, fn.Parameters, fn.AsteriskToken - case ast.KindArrowFunction: - fn := node.AsArrowFunction() - return fn.TypeParameters, fn.Parameters, fn.AsteriskToken - default: - fn := node.AsFunctionDeclaration() - return fn.TypeParameters, fn.Parameters, fn.AsteriskToken - } -} - -func (tx *DeclarationTransformer) tryGetPropertyName(node *ast.Node) string { - if ast.IsElementAccessExpression(node) { - return tx.resolver.GetElementAccessExpressionName(node.AsElementAccessExpression()) - } - if ast.IsPropertyAccessExpression(node) { - return node.Name().Text() - } - return "" -} diff --git a/kitcom/internal/tsgo/transformers/declarations/util.go b/kitcom/internal/tsgo/transformers/declarations/util.go deleted file mode 100644 index 68f30db..0000000 --- a/kitcom/internal/tsgo/transformers/declarations/util.go +++ /dev/null @@ -1,236 +0,0 @@ -package declarations - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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.IsParameter(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) // || // !!! TODO: JSDoc support - /* ast.IsJSDocTypeAlias(node); */ -} - -func hasInferredType(node *ast.Node) bool { - // Debug.type(node); // !!! - switch node.Kind { - case ast.KindParameter, - ast.KindPropertySignature, - ast.KindPropertyDeclaration, - ast.KindBindingElement, - ast.KindPropertyAccessExpression, - ast.KindElementAccessExpression, - ast.KindBinaryExpression, - ast.KindVariableDeclaration, - ast.KindExportAssignment, - ast.KindJSExportAssignment, - ast.KindPropertyAssignment, - ast.KindShorthandPropertyAssignment, - ast.KindJSDocParameterTag, - ast.KindJSDocPropertyTag: - return true - default: - // assertType(node); // !!! - return false - } -} - -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.KindJSExportAssignment, - 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().AsBindingPattern().Elements.Nodes { - 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) -} - -func isAlwaysType(node *ast.Node) bool { - if node.Kind == ast.KindInterfaceDeclaration { - return true - } - return false -} - -func maskModifierFlags(host DeclarationEmitHost, node *ast.Node, modifierMask ast.ModifierFlags, modifierAdditions ast.ModifierFlags) ast.ModifierFlags { - flags := host.GetEffectiveDeclarationFlags(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 isPrimitiveLiteralValue(node *ast.Node, includeBigInt bool) bool { - // !!! Debug.type(node); - switch node.Kind { - case ast.KindTrueKeyword, - ast.KindFalseKeyword, - ast.KindNumericLiteral, - ast.KindStringLiteral, - ast.KindNoSubstitutionTemplateLiteral: - return true - case ast.KindBigIntLiteral: - return includeBigInt - case ast.KindPrefixUnaryExpression: - if node.AsPrefixUnaryExpression().Operator == ast.KindMinusToken { - return ast.IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) || (includeBigInt && ast.IsBigIntLiteral(node.AsPrefixUnaryExpression().Operand)) - } - if node.AsPrefixUnaryExpression().Operator == ast.KindPlusToken { - return ast.IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) - } - return false - default: - // !!! assertType(node); - return false - } -} - -func isPrivateMethodTypeParameter(host DeclarationEmitHost, node *ast.TypeParameterDeclaration) bool { - return node.AsNode().Parent.Kind == ast.KindMethodDeclaration && host.GetEffectiveDeclarationFlags(node.AsNode().Parent, ast.ModifierFlagsPrivate) != 0 -} - -// If the ExpandoFunctionDeclaration have multiple overloads, then we only need to emit properties for the last one. -func shouldEmitFunctionProperties(input *ast.FunctionDeclaration) bool { - if input.Body != nil { // if it has an implementation, it must be the last one - return true - } - - overloadSignatures := core.Filter(input.Symbol.Declarations, func(decl *ast.Node) bool { - return ast.IsFunctionDeclaration(decl) - }) - - return len(overloadSignatures) == 0 || overloadSignatures[len(overloadSignatures)-1] == input.AsNode() -} - -func getFirstConstructorWithBody(node *ast.Node) *ast.Node { - for _, member := range node.Members() { - if ast.IsConstructorDeclaration(member) && ast.NodeIsPresent(member.Body()) { - return member - } - } - return 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) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/async.go b/kitcom/internal/tsgo/transformers/estransforms/async.go deleted file mode 100644 index 05c39f9..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/async.go +++ /dev/null @@ -1,19 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type asyncTransformer struct { - transformers.Transformer -} - -func (ch *asyncTransformer) visit(node *ast.Node) *ast.Node { - return node // !!! -} - -func newAsyncTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - tx := &asyncTransformer{} - return tx.NewTransformer(tx.visit, opts.Context) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/classfields.go b/kitcom/internal/tsgo/transformers/estransforms/classfields.go deleted file mode 100644 index 97e28b5..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/classfields.go +++ /dev/null @@ -1,19 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type classFieldsTransformer struct { - transformers.Transformer -} - -func (ch *classFieldsTransformer) visit(node *ast.Node) *ast.Node { - return node // !!! -} - -func newClassFieldsTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - tx := &classFieldsTransformer{} - return tx.NewTransformer(tx.visit, opts.Context) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/classstatic.go b/kitcom/internal/tsgo/transformers/estransforms/classstatic.go deleted file mode 100644 index 3e400e3..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/classstatic.go +++ /dev/null @@ -1,19 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type classStaticBlockTransformer struct { - transformers.Transformer -} - -func (ch *classStaticBlockTransformer) visit(node *ast.Node) *ast.Node { - return node // !!! -} - -func newClassStaticBlockTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - tx := &classStaticBlockTransformer{} - return tx.NewTransformer(tx.visit, opts.Context) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/classthis.go b/kitcom/internal/tsgo/transformers/estransforms/classthis.go deleted file mode 100644 index d221055..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/classthis.go +++ /dev/null @@ -1,28 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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.AsExpressionStatement().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 -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/definitions.go b/kitcom/internal/tsgo/transformers/estransforms/definitions.go deleted file mode 100644 index 88e7d95..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/definitions.go +++ /dev/null @@ -1,46 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -// !!! TODO: This fixed layering scheme assumes you can't swap out the es decorator transform for the legacy one, -// or the proper es class field transform for the legacy one -var ( - NewESNextTransformer = transformers.Chain(newESDecoratorTransformer, newUsingDeclarationTransformer) - // 2025: only module system syntax (import attributes, json modules), untransformed regex modifiers - // 2024: no new downlevel syntax - // 2023: no new downlevel syntax - NewES2022Transformer = transformers.Chain(NewESNextTransformer, newClassStaticBlockTransformer, newClassFieldsTransformer) // !!! top level await? not transformed, just errored on at lower targets - also more of a module system feature anyway - NewES2021Transformer = transformers.Chain(NewES2022Transformer, newLogicalAssignmentTransformer) // !!! numeric seperators? always elided by printer? - NewES2020Transformer = transformers.Chain(NewES2021Transformer, newNullishCoalescingTransformer, newOptionalChainTransformer) // also dynamic import - module system feature - NewES2019Transformer = transformers.Chain(NewES2020Transformer, newOptionalCatchTransformer) - NewES2018Transformer = transformers.Chain(NewES2019Transformer, newObjectRestSpreadTransformer, newforawaitTransformer) - 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 nil // no transforms needed - case /*core.ScriptTargetES2025,*/ core.ScriptTargetES2024, core.ScriptTargetES2023, core.ScriptTargetES2022: - return NewESNextTransformer(opts) - case core.ScriptTargetES2021: - return NewES2022Transformer(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) - } -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/esdecorator.go b/kitcom/internal/tsgo/transformers/estransforms/esdecorator.go deleted file mode 100644 index 464030f..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/esdecorator.go +++ /dev/null @@ -1,19 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type esDecoratorTransformer struct { - transformers.Transformer -} - -func (ch *esDecoratorTransformer) visit(node *ast.Node) *ast.Node { - return node // !!! -} - -func newESDecoratorTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - tx := &esDecoratorTransformer{} - return tx.NewTransformer(tx.visit, opts.Context) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/exponentiation.go b/kitcom/internal/tsgo/transformers/estransforms/exponentiation.go deleted file mode 100644 index 44b6ca8..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/exponentiation.go +++ /dev/null @@ -1,90 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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.AsElementAccessExpression().Expression) - objExpr.Loc = left.AsElementAccessExpression().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) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/forawait.go b/kitcom/internal/tsgo/transformers/estransforms/forawait.go deleted file mode 100644 index da2d991..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/forawait.go +++ /dev/null @@ -1,19 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type forawaitTransformer struct { - transformers.Transformer -} - -func (ch *forawaitTransformer) visit(node *ast.Node) *ast.Node { - return node // !!! -} - -func newforawaitTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - tx := &forawaitTransformer{} - return tx.NewTransformer(tx.visit, opts.Context) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/logicalassignment.go b/kitcom/internal/tsgo/transformers/estransforms/logicalassignment.go deleted file mode 100644 index 8d10bf8..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/logicalassignment.go +++ /dev/null @@ -1,113 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/namedevaluation.go b/kitcom/internal/tsgo/transformers/estransforms/namedevaluation.go deleted file mode 100644 index 578873d..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/namedevaluation.go +++ /dev/null @@ -1,560 +0,0 @@ -package estransforms - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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.AsBlock().Statements.Nodes) != 1 { - return false - } - - statement := node.AsClassStaticBlockDeclaration().Body.AsBlock().Statements.Nodes[0] - if ast.IsExpressionStatement(statement) { - expression := statement.AsExpressionStatement().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) -} - -// Indicates whether a property name is the special `__proto__` property. -// Per the ECMA-262 spec, this only matters for property assignments whose name is -// the Identifier `__proto__`, or the string literal `"__proto__"`, but not for -// computed property names. -func isProtoSetter(node *ast.PropertyName) bool { - return ast.IsIdentifier(node) || ast.IsStringLiteral(node) && node.Text() == "__proto__" -} - -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 -} - -// Indicates whether a node is a potential source of an assigned name for a class, function, or arrow function. -func isNamedEvaluationSource(node *ast.Node) bool { - switch node.Kind { - case ast.KindPropertyAssignment: - return !isProtoSetter(node.AsPropertyAssignment().Name()) - case ast.KindShorthandPropertyAssignment: - return node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer != nil - case ast.KindVariableDeclaration: - return ast.IsIdentifier(node.AsVariableDeclaration().Name()) && node.AsVariableDeclaration().Initializer != nil - case ast.KindParameter: - return ast.IsIdentifier(node.AsParameterDeclaration().Name()) && node.AsParameterDeclaration().Initializer != nil && node.AsParameterDeclaration().DotDotDotToken == nil - case ast.KindBindingElement: - return ast.IsIdentifier(node.AsBindingElement().Name()) && node.AsBindingElement().Initializer != nil && node.AsBindingElement().DotDotDotToken == nil - case ast.KindPropertyDeclaration: - return node.AsPropertyDeclaration().Initializer != nil - case ast.KindBinaryExpression: - switch node.AsBinaryExpression().OperatorToken.Kind { - case ast.KindEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindBarBarEqualsToken, ast.KindQuestionQuestionEqualsToken: - return ast.IsIdentifier(node.AsBinaryExpression().Left) - } - break - case ast.KindExportAssignment: - return true - } - return false -} - -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 !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.AsExportAssignment().Expression, cb) - default: - panic("Unhandled case in isNamedEvaluation") - } -} - -// 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*/) *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") - } - 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) - 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 - } - - if !ast.IsComputedPropertyName(expression) { - panic("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().AsBlock().Statements.Nodes[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 - - 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) - return node -} - -func finishTransformNamedEvaluation( - emitContext *printer.EmitContext, - expression *ast.Node, // WrappedExpression, - 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) - } 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) - } 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) - } 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) - } 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) - } 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) - } else { - assignedName = factory.NewStringLiteral("") - } - expression := finishTransformNamedEvaluation(emitContext, node.Expression, assignedName, ignoreEmptyStringLiteral) - return factory.UpdateExportAssignment( - node, - nil, /*modifiers*/ - 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: - panic("Unhandled case in transformNamedEvaluation") - } -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/nullishcoalescing.go b/kitcom/internal/tsgo/transformers/estransforms/nullishcoalescing.go deleted file mode 100644 index 4b689c6..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/nullishcoalescing.go +++ /dev/null @@ -1,49 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/objectrestspread.go b/kitcom/internal/tsgo/transformers/estransforms/objectrestspread.go deleted file mode 100644 index 5bd83f6..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/objectrestspread.go +++ /dev/null @@ -1,1108 +0,0 @@ -package estransforms - -import ( - "strconv" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type pendingDecl struct { - pendingExpressions []*ast.Node - name *ast.Node - value *ast.Node - location core.TextRange - original *ast.Node -} - -type flattenLevel int - -const ( - flattenLevelAll flattenLevel = iota - flattenLevelObjectRest -) - -type flattenContext struct { - level flattenLevel - currentExpressions []*ast.Node - currentDeclarations []pendingDecl - hasTransformedPriorElement bool - emitBindingOrAssignment func(t *objectRestSpreadTransformer, target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node) - createArrayBindingOrAssignmentPattern func(t *objectRestSpreadTransformer, elements []*ast.Node) *ast.Node - createObjectBindingOrAssignmentPattern func(t *objectRestSpreadTransformer, elements []*ast.Node) *ast.Node - createArrayBindingOrAssignmentElement func(t *objectRestSpreadTransformer, expr *ast.Node) *ast.Node - hoistTempVariables bool -} - -type oldFlattenContext flattenContext - -type objectRestSpreadTransformer struct { - transformers.Transformer - compilerOptions *core.CompilerOptions - - inExportedVariableStatement bool - - ctx flattenContext - parametersWithPrecedingObjectRestOrSpread map[*ast.Node]struct{} -} - -func (ch *objectRestSpreadTransformer) enterFlattenContext( - level flattenLevel, - emitBindingOrAssignment func(t *objectRestSpreadTransformer, target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node), - createArrayBindingOrAssignmentPattern func(t *objectRestSpreadTransformer, elements []*ast.Node) *ast.Node, - createObjectBindingOrAssignmentPattern func(t *objectRestSpreadTransformer, elements []*ast.Node) *ast.Node, - createArrayBindingOrAssignmentElement func(t *objectRestSpreadTransformer, expr *ast.Node) *ast.Node, - hoistTempVariables bool, -) oldFlattenContext { - old := ch.ctx - ch.ctx = flattenContext{ - level: level, - emitBindingOrAssignment: emitBindingOrAssignment, - createArrayBindingOrAssignmentPattern: createArrayBindingOrAssignmentPattern, - createObjectBindingOrAssignmentPattern: createObjectBindingOrAssignmentPattern, - createArrayBindingOrAssignmentElement: createArrayBindingOrAssignmentElement, - hoistTempVariables: hoistTempVariables, - } - return oldFlattenContext(old) -} - -func (ch *objectRestSpreadTransformer) exitFlattenContext(old oldFlattenContext) { - ch.ctx = flattenContext(old) -} - -func (ch *objectRestSpreadTransformer) visit(node *ast.Node) *ast.Node { - if node.SubtreeFacts()&ast.SubtreeContainsESObjectRestOrSpread == 0 && ch.parametersWithPrecedingObjectRestOrSpread == nil { - return node - } - 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()) - 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 { - 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.AsBlock().Statements.Nodes { - 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.AsBlock().Statements.Nodes[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.AsBlock().Statements.Loc - return ch.Factory().UpdateBlock(body.AsBlock(), newStatementList) -} - -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().AsBindingPattern().Elements.Nodes) > 0 { - declarations := ch.flattenDestructuringBinding(flattenLevelAll, parameter, ch.Factory().NewGeneratedNameForNode(parameter), false, false) - if declarations != nil { - declarationList := ch.Factory().NewVariableDeclarationList(ast.NodeFlagsNone, ch.Factory().NewNodeList([]*ast.Node{})) - 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 := ch.flattenDestructuringBinding(flattenLevelObjectRest, parameter, ch.Factory().NewGeneratedNameForNode(parameter), false, true) - if declarations != nil { - declarationList := ch.Factory().NewVariableDeclarationList(ast.NodeFlagsNone, ch.Factory().NewNodeList([]*ast.Node{})) - 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 := ch.flattenDestructuringBinding(flattenLevelObjectRest, updatedDecl, nil, false, false) - block := ch.Visitor().VisitNode(node.Block) - if visitedBindings != nil { - var decls []*ast.Node - if visitedBindings.Kind&ast.KindSyntaxList != 0 { - decls = visitedBindings.AsSyntaxList().Children - } else { - decls = []*ast.Node{visitedBindings} - } - newStatement := ch.Factory().NewVariableStatement(nil, ch.Factory().NewVariableDeclarationList(ast.NodeFlagsNone, ch.Factory().NewNodeList(decls))) - statements := []*ast.Node{newStatement} - if block.AsBlock().Statements != nil && len(block.AsBlock().Statements.Nodes) > 0 { - statements = append(statements, block.AsBlock().Statements.Nodes...) - } - statementList := ch.Factory().NewNodeList(statements) - statementList.Loc = block.AsBlock().Statements.Loc - - block = ch.Factory().UpdateBlock(block.AsBlock(), statementList) - } - 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 ch.flattenDestructuringBinding( - flattenLevelObjectRest, - node.AsNode(), - nil, - exported, - false, - ) - } - return ch.Visitor().VisitEachChild(node.AsNode()) -} - -func (ch *objectRestSpreadTransformer) flattenDestructuringBinding(level flattenLevel, node *ast.Node, rvalue *ast.Node, hoist bool, skipInitializer bool) *ast.Node { - old := ch.enterFlattenContext(level, (*objectRestSpreadTransformer).emitBinding, (*objectRestSpreadTransformer).createArrayBindingPattern, (*objectRestSpreadTransformer).createObjectBindingPattern, (*objectRestSpreadTransformer).createArrayBindingElement, hoist) - defer ch.exitFlattenContext(old) - - if ast.IsVariableDeclaration(node) { - initializer := getInitializerOfBindingOrAssignmentElement(node) - if initializer != nil && (ast.IsIdentifier(initializer) && bindingOrAssignmentElementAssignsToName(node, initializer.AsIdentifier().Text) || bindingOrAssignmentElementContainsNonLiteralComputedName(node)) { - // If the right-hand value of the assignment is also an assignment target then - // we need to cache the right-hand value. - initializer = ch.ensureIdentifier(ch.Visitor().VisitNode(initializer), false, initializer.Loc) - node = ch.Factory().UpdateVariableDeclaration(node.AsVariableDeclaration(), node.Name(), nil, nil, initializer) - } - } - - ch.flattenBindingOrAssignmentElement(node, rvalue, node.Loc, skipInitializer) - - if len(ch.ctx.currentExpressions) > 0 { - temp := ch.Factory().NewTempVariable() - ch.EmitContext().AddVariableDeclaration(temp) - last := &ch.ctx.currentDeclarations[len(ch.ctx.currentDeclarations)-1] - last.pendingExpressions = append(last.pendingExpressions, ch.Factory().NewAssignmentExpression(temp, last.value)) - last.pendingExpressions = append(last.pendingExpressions, ch.ctx.currentExpressions...) - last.value = temp - } - decls := make([]*ast.Node, 0, len(ch.ctx.currentDeclarations)) - for _, pending := range ch.ctx.currentDeclarations { - expr := pending.value - if len(pending.pendingExpressions) > 0 { - expr = ch.Factory().InlineExpressions(append(pending.pendingExpressions, pending.value)) - } - decl := ch.Factory().NewVariableDeclaration( - pending.name, - nil, - nil, - expr, - ) - decl.Loc = pending.location - if pending.original != nil { - ch.EmitContext().SetOriginal(decl, pending.original) - } - decls = append(decls, decl) - } - if len(decls) == 1 { - return decls[0] - } - if len(decls) == 0 { - return nil - } - return ch.Factory().NewSyntaxList(decls) -} - -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.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.AsBlock().Statements.Nodes { - visited := ch.Visitor().VisitEachChild(statement) - if visited != nil { - statements = append(statements, visited) - } - } - bodyLocation = node.Statement.Loc - statementsLocation = node.Statement.AsBlock().Statements.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( - ast.NodeFlagsLet, - ch.Factory().NewNodeList([]*ast.Node{ch.Factory().NewVariableDeclaration(temp, nil, nil, nil)}), - ) - 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) createForOfBindingStatement(node *ast.Node, boundValue *ast.Node) *ast.Node { - if ast.IsVariableDeclarationList(node) { - firstDeclaration := node.AsVariableDeclarationList().Declarations.Nodes[0] - updatedDeclaration := ch.Factory().UpdateVariableDeclaration( - firstDeclaration.AsVariableDeclaration(), - firstDeclaration.Name(), - nil, - nil, - boundValue, - ) - statement := ch.Factory().NewVariableStatement( - nil, - ch.Factory().UpdateVariableDeclarationList( - node.AsVariableDeclarationList(), - ch.Factory().NewNodeList([]*ast.Node{updatedDeclaration}), - ), - ) - statement.Loc = node.Loc - return statement - } else { - updatedExpression := ch.Factory().NewAssignmentExpression(node, boundValue) - updatedExpression.Loc = node.Loc - statement := ch.Factory().NewExpressionStatement(updatedExpression) - statement.Loc = node.Loc - return statement - } -} - -func (ch *objectRestSpreadTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node { - if !(ast.IsDestructuringAssignment(node.AsNode()) && ast.ContainsObjectRestOrSpread(node.Left)) { - return ch.Visitor().VisitEachChild(node.AsNode()) - } - return ch.flattenDestructuringAssignment( - node, - ) -} - -func (ch *objectRestSpreadTransformer) flattenDestructuringAssignment(node *ast.BinaryExpression) *ast.Node { - location := node.Loc - var value *ast.Node - if ast.IsDestructuringAssignment(node.AsNode()) { - value = node.Right - for ast.IsEmptyArrayLiteral(node.Left) || ast.IsEmptyObjectLiteral(node.Left) { - if ast.IsDestructuringAssignment(value) { - node = value.AsBinaryExpression() - location = node.Loc - value = node.Right - } else { - return ch.Visitor().VisitNode(value) - } - } - } - old := ch.enterFlattenContext(flattenLevelObjectRest, (*objectRestSpreadTransformer).emitAssignment, (*objectRestSpreadTransformer).createArrayAssignmentPattern, (*objectRestSpreadTransformer).createObjectAssignmentPattern, (*objectRestSpreadTransformer).createArrayAssignmentElement, true) - defer ch.exitFlattenContext(old) - - if value != nil { - value = ch.Visitor().VisitNode(value) - - if ast.IsIdentifier(value) && bindingOrAssignmentElementAssignsToName(node.AsNode(), value.AsIdentifier().Text) || bindingOrAssignmentElementContainsNonLiteralComputedName(node.AsNode()) { - // If the right-hand value of the assignment is also an assignment target then - // we need to cache the right-hand value. - value = ch.ensureIdentifier(value, false, location) - } else { - value = ch.ensureIdentifier(value, true, location) - } - - if ast.NodeIsSynthesized(node.AsNode()) { - // Generally, the source map location for a destructuring assignment is the root - // expression. - // - // However, if the root expression is synthesized (as in the case - // of the initializer when transforming a ForOfStatement), then the source map - // location should point to the right-hand value of the expression. - location = value.Loc - } - } - - ch.flattenBindingOrAssignmentElement(node.AsNode(), value, location, ast.IsDestructuringAssignment(node.AsNode())) - - res := ch.Factory().InlineExpressions(ch.ctx.currentExpressions) - if res != nil { - return res - } - return ch.Factory().NewOmittedExpression() -} - -func (ch *objectRestSpreadTransformer) flattenBindingOrAssignmentElement(element *ast.Node, value *ast.Node, location core.TextRange, skipInitializer bool) { - bindingTarget := ast.GetTargetOfBindingOrAssignmentElement(element) - if !skipInitializer { - initializer := ch.Visitor().VisitNode(getInitializerOfBindingOrAssignmentElement(element)) - if initializer != nil { - // Combine value and initializer - if value != nil { - value = ch.createDefaultValueCheck(value, initializer, location) - // If 'value' is not a simple expression, it could contain side-effecting code that should evaluate before an object or array binding pattern. - if !transformers.IsSimpleCopiableExpression(initializer) && (ast.IsBindingPattern(bindingTarget) || ast.IsAssignmentPattern(bindingTarget)) { - value = ch.ensureIdentifier(value, true, location) - } - } else { - value = initializer - } - } else if value == nil { - // Use 'void 0' in absence of value and initializer - value = ch.Factory().NewVoidZeroExpression() - } - } - - if isObjectBindingOrAssignmentPattern(bindingTarget) { - ch.flattenObjectBindingOrAssignmentPattern(element, bindingTarget, value, location) - } else if isArrayBindingOrAssignmentPattern(bindingTarget) { - ch.flattenArrayBindingOrAssignmentPattern(element, bindingTarget, value, location) - } else { - ch.ctx.emitBindingOrAssignment(ch, bindingTarget, value, location, element) - } -} - -func (ch *objectRestSpreadTransformer) flattenObjectBindingOrAssignmentPattern(parent *ast.Node, pattern *ast.Node, value *ast.Node, location core.TextRange) { - elements := ast.GetElementsOfBindingOrAssignmentPattern(pattern) - numElements := len(elements) - if numElements != 1 { - // For anything other than a single-element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. Additionally, if we have zero elements - // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, - // so in that case, we'll intentionally create that temporary. - reuseIdentifierExpressions := !ast.IsDeclarationBindingElement(parent) || numElements != 0 - value = ch.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 ch.ctx.level >= flattenLevelObjectRest && element.SubtreeFacts()&(ast.SubtreeContainsRestOrSpread|ast.SubtreeContainsObjectRestOrSpread) == 0 && ast.GetTargetOfBindingOrAssignmentElement(element).SubtreeFacts()&(ast.SubtreeContainsRestOrSpread|ast.SubtreeContainsObjectRestOrSpread) == 0 && !ast.IsComputedPropertyName(propertyName) { - bindingElements = append(bindingElements, ch.Visitor().VisitNode(element)) - } else { - if len(bindingElements) > 0 { - ch.ctx.emitBindingOrAssignment(ch, ch.ctx.createObjectBindingOrAssignmentPattern(ch, bindingElements), value, location, pattern) - bindingElements = nil - } - rhsValue := ch.createDestructuringPropertyAccess(value, propertyName) - if ast.IsComputedPropertyName(propertyName) { - computedTempVariables = append(computedTempVariables, rhsValue.AsElementAccessExpression().ArgumentExpression) - } - ch.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false) - } - } else if i == numElements-1 { - if len(bindingElements) > 0 { - ch.ctx.emitBindingOrAssignment(ch, ch.ctx.createObjectBindingOrAssignmentPattern(ch, bindingElements), value, location, pattern) - bindingElements = nil - } - rhsValue := ch.Factory().NewRestHelper(value, elements, computedTempVariables, pattern.Loc) - ch.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false) - } - } - if len(bindingElements) > 0 { - ch.ctx.emitBindingOrAssignment(ch, ch.ctx.createObjectBindingOrAssignmentPattern(ch, bindingElements), value, location, pattern) - } -} - -type restIdElemPair struct { - id *ast.Node - element *ast.Node -} - -func (ch *objectRestSpreadTransformer) flattenArrayBindingOrAssignmentPattern(parent *ast.Node, pattern *ast.Node, value *ast.Node, location core.TextRange) { - elements := ast.GetElementsOfBindingOrAssignmentPattern(pattern) - numElements := len(elements) - if numElements != 1 && (ch.ctx.level < flattenLevelObjectRest || numElements == 0) || core.Every(elements, ast.IsOmittedExpression) { - // For anything other than a single-element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. Additionally, if we have zero elements - // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, - // so in that case, we'll intentionally create that temporary. - // Or all the elements of the binding pattern are omitted expression such as "var [,] = [1,2]", - // then we will create temporary variable. - reuseIdentifierExpressions := !ast.IsDeclarationBindingElement(parent) || numElements != 0 - value = ch.ensureIdentifier(value, reuseIdentifierExpressions, location) - } - var bindingElements []*ast.Node - var restContainingElements []restIdElemPair - for i, element := range elements { - if ch.ctx.level >= flattenLevelObjectRest { - // If an array pattern contains an ObjectRest, we must cache the result so that we - // can perform the ObjectRest destructuring in a different declaration - if element.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 || ch.ctx.hasTransformedPriorElement && !isSimpleBindingOrAssignmentElement(element) { - ch.ctx.hasTransformedPriorElement = true - temp := ch.Factory().NewTempVariable() - if ch.ctx.hoistTempVariables { - ch.EmitContext().AddVariableDeclaration(temp) - } - - restContainingElements = append(restContainingElements, restIdElemPair{temp, element}) - bindingElements = append(bindingElements, ch.ctx.createArrayBindingOrAssignmentElement(ch, temp)) - } else { - bindingElements = append(bindingElements, element) - } - } else if ast.IsOmittedExpression(element) { - continue - } else if ast.GetRestIndicatorOfBindingOrAssignmentElement(element) == nil { - rhsValue := ch.Factory().NewElementAccessExpression(value, nil, ch.Factory().NewNumericLiteral(strconv.Itoa(i)), ast.NodeFlagsNone) - ch.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false) - } else if i == numElements-1 { - rhsValue := ch.Factory().NewArraySliceCall(value, i) - ch.flattenBindingOrAssignmentElement(element, rhsValue, element.Loc, false) - } - } - if len(bindingElements) > 0 { - ch.ctx.emitBindingOrAssignment(ch, ch.ctx.createArrayBindingOrAssignmentPattern(ch, bindingElements), value, location, pattern) - } - if len(restContainingElements) > 0 { - for _, pair := range restContainingElements { - ch.flattenBindingOrAssignmentElement(pair.element, pair.id, pair.element.Loc, false) - } - } -} - -/** - * Creates either a PropertyAccessExpression or an ElementAccessExpression for the - * right-hand side of a transformed destructuring assignment. - * - * @link https://tc39.github.io/ecma262/#sec-runtime-semantics-keyeddestructuringassignmentevaluation - * - * @param flattenContext Options used to control flattening. - * @param value The RHS value that is the source of the property. - * @param propertyName The destructuring property name. - */ -func (ch *objectRestSpreadTransformer) createDestructuringPropertyAccess(value *ast.Node, propertyName *ast.Node) *ast.Node { - if ast.IsComputedPropertyName(propertyName) { - argumentExpression := ch.ensureIdentifier(ch.Visitor().VisitNode(propertyName.AsComputedPropertyName().Expression), false, propertyName.Loc) - return ch.Factory().NewElementAccessExpression( - value, - nil, - argumentExpression, - ast.NodeFlagsNone, - ) - } else if ast.IsStringOrNumericLiteralLike(propertyName) || ast.IsBigIntLiteral(propertyName) { - argumentExpression := propertyName.Clone(ch.Factory()) - return ch.Factory().NewElementAccessExpression( - value, - nil, - argumentExpression, - ast.NodeFlagsNone, - ) - } else { - name := ch.Factory().NewIdentifier(propertyName.AsIdentifier().Text) - return ch.Factory().NewPropertyAccessExpression( - value, - nil, - name, - ast.NodeFlagsNone, - ) - } -} - -func (ch *objectRestSpreadTransformer) createObjectBindingPattern(elements []*ast.Node) *ast.Node { - return ch.Factory().NewBindingPattern(ast.KindObjectBindingPattern, ch.Factory().NewNodeList(elements)) -} - -func (ch *objectRestSpreadTransformer) createArrayBindingPattern(elements []*ast.Node) *ast.Node { - return ch.Factory().NewBindingPattern(ast.KindArrayBindingPattern, ch.Factory().NewNodeList(elements)) -} - -func (ch *objectRestSpreadTransformer) createObjectAssignmentPattern(elements []*ast.Node) *ast.Node { - return ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(elements), false) -} - -func (ch *objectRestSpreadTransformer) createArrayAssignmentPattern(elements []*ast.Node) *ast.Node { - return ch.Factory().NewArrayLiteralExpression(ch.Factory().NewNodeList(elements), false) -} - -func (ch *objectRestSpreadTransformer) createArrayAssignmentElement(expr *ast.Node) *ast.Node { - return expr -} - -func (ch *objectRestSpreadTransformer) createArrayBindingElement(expr *ast.Node) *ast.Node { - return ch.Factory().NewBindingElement(nil, nil, expr, nil) -} - -func (ch *objectRestSpreadTransformer) emitExpression(node *ast.Node) { - ch.ctx.currentExpressions = append(ch.ctx.currentExpressions, node) -} - -func (ch *objectRestSpreadTransformer) emitAssignment(target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node) { - debug.AssertNode(target, ast.IsExpression) - expr := ch.Factory().NewAssignmentExpression(ch.Visitor().VisitNode(target), value) - expr.Loc = location - ch.EmitContext().SetOriginal(expr, original) - ch.emitExpression(expr) -} - -func isBindingName(node *ast.Node) bool { - return node.Kind == ast.KindIdentifier || node.Kind == ast.KindArrayBindingPattern || node.Kind == ast.KindObjectBindingPattern -} - -func (ch *objectRestSpreadTransformer) emitBinding(target *ast.Node, value *ast.Node, location core.TextRange, original *ast.Node) { - debug.AssertNode(target, isBindingName) - if len(ch.ctx.currentExpressions) > 0 { - value = ch.Factory().InlineExpressions(append(ch.ctx.currentExpressions, value)) - ch.ctx.currentExpressions = nil - } - ch.ctx.currentDeclarations = append(ch.ctx.currentDeclarations, pendingDecl{ - ch.ctx.currentExpressions, - target, - value, - location, - original, - }) -} - -func (ch *objectRestSpreadTransformer) ensureIdentifier(value *ast.Node, reuseIdentifierExpressions bool, location core.TextRange) *ast.Node { - if reuseIdentifierExpressions && ast.IsIdentifier(value) { - return value - } - - temp := ch.Factory().NewTempVariable() - if ch.ctx.hoistTempVariables { - ch.EmitContext().AddVariableDeclaration(temp) - assign := ch.Factory().NewAssignmentExpression(temp, value) - assign.Loc = location - ch.emitExpression(assign) - } else { - ch.ctx.emitBindingOrAssignment(ch, temp, value, location, nil) - } - return temp -} - -func (ch *objectRestSpreadTransformer) createDefaultValueCheck(value *ast.Expression, defaultValue *ast.Expression, location core.TextRange) *ast.Node { - value = ch.ensureIdentifier(value, true, location) - return ch.Factory().NewConditionalExpression( - ch.Factory().NewTypeCheck(value, "undefined"), - ch.Factory().NewToken(ast.KindQuestionToken), - defaultValue, - ch.Factory().NewToken(ast.KindColonToken), - value, - ) -} - -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) -} - -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.AsIdentifier().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 -} - -func bindingOrAssignmentElementContainsNonLiteralComputedName(element *ast.Node) bool { - propertyName := ast.TryGetPropertyNameOfBindingOrAssignmentElement(element) - if propertyName != nil && ast.IsComputedPropertyName(propertyName) && !ast.IsLiteralExpression(propertyName.AsComputedPropertyName().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) - for _, element := range elements { - if bindingOrAssignmentElementContainsNonLiteralComputedName(element) { - return true - } - } - return false -} - -func getInitializerOfBindingOrAssignmentElement(bindingElement *ast.Node) *ast.Node { - if ast.IsDeclarationBindingElement(bindingElement) { - // `1` in `let { a = 1 } = ...` - // `1` in `let { a: b = 1 } = ...` - // `1` in `let { a: {b} = 1 } = ...` - // `1` in `let { a: [b] = 1 } = ...` - // `1` in `let [a = 1] = ...` - // `1` in `let [{a} = 1] = ...` - // `1` in `let [[a] = 1] = ...` - return bindingElement.Initializer() - } - - if ast.IsPropertyAssignment(bindingElement) { - // `1` in `({ a: b = 1 } = ...)` - // `1` in `({ a: {b} = 1 } = ...)` - // `1` in `({ a: [b] = 1 } = ...)` - initializer := bindingElement.Initializer() - if ast.IsAssignmentExpression(initializer, true) { - return initializer.AsBinaryExpression().Right - } - return nil - } - - if ast.IsShorthandPropertyAssignment(bindingElement) { - // `1` in `({ a = 1 } = ...)` - return bindingElement.AsShorthandPropertyAssignment().ObjectAssignmentInitializer - } - - if ast.IsAssignmentExpression(bindingElement, true) { - // `1` in `[a = 1] = ...` - // `1` in `[{a} = 1] = ...` - // `1` in `[[a] = 1] = ...` - return bindingElement.AsBinaryExpression().Right - } - - if ast.IsSpreadElement(bindingElement) { - // Recovery consistent with existing emit. - return getInitializerOfBindingOrAssignmentElement(bindingElement.Expression()) - } - return nil -} - -func isObjectBindingOrAssignmentPattern(node *ast.Node) bool { - return node.Kind == ast.KindObjectBindingPattern || node.Kind == ast.KindObjectLiteralExpression -} - -func isArrayBindingOrAssignmentPattern(node *ast.Node) bool { - return 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 && !transformers.IsSimpleInlineableExpression(initializer) { - return false - } - if ast.IsBindingPattern(target) || ast.IsAssignmentPattern(target) { - return core.Every(ast.GetElementsOfBindingOrAssignmentPattern(target), isSimpleBindingOrAssignmentElement) - } - return ast.IsIdentifier(target) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/optionalcatch.go b/kitcom/internal/tsgo/transformers/estransforms/optionalcatch.go deleted file mode 100644 index 3e5cff9..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/optionalcatch.go +++ /dev/null @@ -1,37 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/optionalchain.go b/kitcom/internal/tsgo/transformers/estransforms/optionalchain.go deleted file mode 100644 index 21aec59..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/optionalchain.go +++ /dev/null @@ -1,240 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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, nil, args) - } - } - 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.AssertNotNode(expression, ast.IsSyntheticReferenceExpression) - - 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, ch.Visitor().VisitNode(p.Name())) - } else { - p := node.AsElementAccessExpression() - expression = ch.Factory().UpdateElementAccessExpression(p, expression, nil, ch.Visitor().VisitNode(p.AsElementAccessExpression().ArgumentExpression)) - } - - 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.AssertNotNode(chain, isNonNullChain) - links := []*ast.Node{chain} - for !ast.IsTaggedTemplateExpression(chain) && chain.QuestionDotToken() == nil { - chain = ast.SkipPartiallyEmittedExpressions(chain.Expression()) - debug.AssertNotNode(chain, isNonNullChain) - 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) -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/using.go b/kitcom/internal/tsgo/transformers/estransforms/using.go deleted file mode 100644 index 5b80403..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/using.go +++ /dev/null @@ -1,867 +0,0 @@ -package estransforms - -import ( - "maps" - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" -) - -type usingDeclarationTransformer struct { - transformers.Transformer - - exportBindings map[string]*ast.ExportSpecifierNode - 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()) - case ast.KindSwitchStatement: - node = tx.visitSwitchStatement(node.AsSwitchStatement()) - 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 { - topLevelStatements = append( - topLevelStatements, - tx.Factory().NewExportDeclaration( - nil, /*modifiers*/ - false, /*isTypeOnly*/ - tx.Factory().NewNamedExports( - tx.Factory().NewNodeList( - slices.Collect(maps.Values(tx.exportBindings)), - ), - ), - nil, /*moduleSpecifier*/ - nil, /*attributes*/ - ), - ) - } - - topLevelStatements = tx.EmitContext().EndAndMergeVariableEnvironment(topLevelStatements) - if len(tx.exportVars) > 0 { - topLevelStatements = append(topLevelStatements, tx.Factory().NewVariableStatement( - tx.Factory().NewModifierList([]*ast.Node{ - tx.Factory().NewModifier(ast.KindExportKeyword), - }), - tx.Factory().NewVariableDeclarationList( - ast.NodeFlagsLet, - tx.Factory().NewNodeList(tx.exportVars), - ), - )) - } - 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.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) - } - 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( - core.IfElse(isAwaitUsing, ast.NodeFlagsAwaitUsing, ast.NodeFlagsUsing), - tx.Factory().NewNodeList([]*ast.Node{usingVar}), - ) - usingVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, usingVarList) - var statement *ast.Statement - if ast.IsBlock(node.Statement) { - statements := make([]*ast.Statement, 0, len(node.Statement.AsBlock().Statements.Nodes)+1) - statements = append(statements, usingVarStatement) - statements = append(statements, node.Statement.AsBlock().Statements.Nodes...) - statement = tx.Factory().UpdateBlock( - node.Statement.AsBlock(), - tx.Factory().NewNodeList(statements), - ) - } 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( - ast.NodeFlagsConst, - tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{ - tx.Factory().NewVariableDeclaration(temp, nil /*exclamationToken*/, nil /*type*/, nil), - }), - ), - node.Expression, - statement, - ), - ) - } - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -func (tx *usingDeclarationTransformer) visitCaseOrDefaultClause(node *ast.CaseOrDefaultClause, envBinding *ast.IdentifierNode) *ast.Node { - if getUsingKindOfStatements(node.Statements.Nodes) != usingKindNone { - return tx.Factory().UpdateCaseOrDefaultClause( - node, - tx.Visitor().VisitNode(node.Expression), - tx.Factory().NewNodeList(tx.transformUsingDeclarations(node.Statements.Nodes, envBinding, nil /*topLevelStatements*/)), - ) - } - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -func (tx *usingDeclarationTransformer) visitSwitchStatement(node *ast.SwitchStatement) *ast.Node { - // given: - // - // switch (expr) { - // case expr: - // using res = expr; - // } - // - // produces: - // - // const env_1 = { stack: [], error: void 0, hasError: false }; - // try { - // switch(expr) { - // case expr: - // const res = __addDisposableResource(env_1, expr, false); - // } - // } - // catch (e_1) { - // env_1.error = e_1; - // env_1.hasError = true; - // } - // finally { - // __disposeResources(env_1); - // } - // - usingKind := getUsingKindOfCaseOrDefaultClauses(node.CaseBlock.AsCaseBlock().Clauses.Nodes) - if usingKind != usingKindNone { - envBinding := tx.createEnvBinding() - return transformers.SingleOrMany(tx.createDownlevelUsingStatements( - []*ast.Statement{ - tx.Factory().UpdateSwitchStatement( - node, - tx.Visitor().VisitNode(node.Expression), - tx.Factory().UpdateCaseBlock( - node.CaseBlock.AsCaseBlock(), - tx.Factory().NewNodeList( - core.Map(node.CaseBlock.AsCaseBlock().Clauses.Nodes, func(clause *ast.CaseOrDefaultClauseNode) *ast.CaseOrDefaultClauseNode { - return tx.visitCaseOrDefaultClause(clause.AsCaseOrDefaultClause(), envBinding) - }), - ), - ), - ), - }, - envBinding, - usingKind == usingKindAsync, - ), tx.Factory()) - } - - 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(ast.NodeFlagsConst, tx.Factory().NewNodeList(declarations)) - 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|printer.EFInternalName)) - } 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().AsBindingPattern().Elements.Nodes { - 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) - } - 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(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{envVar})) - envVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, envVarList) - statements = append(statements, envVarStatement) - - // when `async` is `false`, produces: - // - // try { - // - // } - // catch (e_1) { - // env_1.error = e_1; - // env_1.hasError = true; - // } - // finally { - // __disposeResources(env_1); - // } - - // when `async` is `true`, produces: - // - // try { - // - // } - // 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(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{ - tx.Factory().NewVariableDeclaration( - result, - nil, /*exclamationToken*/ - nil, /*type*/ - tx.Factory().NewDisposeResourcesHelper(envBinding), - ), - })), - ), - 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 -} - -func getUsingKindOfCaseOrDefaultClauses(clauses []*ast.CaseOrDefaultClauseNode) usingKind { - result := usingKindNone - for _, clause := range clauses { - usingKind := getUsingKindOfStatements(clause.AsCaseOrDefaultClause().Statements.Nodes) - if usingKind == usingKindAsync { - return usingKindAsync - } - if usingKind > result { - result = usingKind - } - } - return result -} diff --git a/kitcom/internal/tsgo/transformers/estransforms/utilities.go b/kitcom/internal/tsgo/transformers/estransforms/utilities.go deleted file mode 100644 index 4650927..0000000 --- a/kitcom/internal/tsgo/transformers/estransforms/utilities.go +++ /dev/null @@ -1,49 +0,0 @@ -package estransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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(), - ), - ) -} diff --git a/kitcom/internal/tsgo/transformers/inliners/constenum.go b/kitcom/internal/tsgo/transformers/inliners/constenum.go deleted file mode 100644 index 0c31c3e..0000000 --- a/kitcom/internal/tsgo/transformers/inliners/constenum.go +++ /dev/null @@ -1,88 +0,0 @@ -package inliners - -import ( - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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()) - } else { - replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewNumericLiteral(v.Abs().String())) - } - case string: - replacement = tx.Factory().NewStringLiteral(v) - 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") - } else if !v.Negative { - replacement = tx.Factory().NewBigIntLiteral(v.Base10Value) - } else { - replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewBigIntLiteral(v.Base10Value)) - } - } - - 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 { - return strings.ReplaceAll(text, "*/", "*_/") -} diff --git a/kitcom/internal/tsgo/transformers/jsxtransforms/jsx.go b/kitcom/internal/tsgo/transformers/jsxtransforms/jsx.go deleted file mode 100644 index e40fc21..0000000 --- a/kitcom/internal/tsgo/transformers/jsxtransforms/jsx.go +++ /dev/null @@ -1,1132 +0,0 @@ -package jsxtransforms - -import ( - "maps" - "slices" - "strconv" - "strings" - "unicode/utf8" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "github.com/dlclark/regexp2" -) - -type JSXTransformer struct { - transformers.Transformer - compilerOptions *core.CompilerOptions - emitResolver printer.EmitResolver - - importSpecifier string - filenameDeclaration *ast.Node - utilizedImplicitRuntimeImports map[string]map[string]*ast.Node - inJsxChild bool - - currentSourceFile *ast.SourceFile -} - -func NewJSXTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - compilerOptions := opts.CompilerOptions - emitContext := opts.Context - tx := &JSXTransformer{ - compilerOptions: compilerOptions, - emitResolver: opts.EmitResolver, - } - return tx.NewTransformer(tx.visit, emitContext) -} - -func (tx *JSXTransformer) getCurrentFileNameExpression() *ast.Node { - if tx.filenameDeclaration != nil { - return tx.filenameDeclaration.AsVariableDeclaration().Name() - } - d := tx.Factory().NewVariableDeclaration( - tx.Factory().NewUniqueNameEx("_jsxFileName", printer.AutoGenerateOptions{ - Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel, - }), - nil, - nil, - tx.Factory().NewStringLiteral(tx.currentSourceFile.FileName()), - ) - tx.filenameDeclaration = d - return d.AsVariableDeclaration().Name() -} - -func (tx *JSXTransformer) getJsxFactoryCalleePrimitive(isStaticChildren bool) string { - if tx.compilerOptions.Jsx == core.JsxEmitReactJSXDev { - return "jsxDEV" - } - if isStaticChildren { - return "jsxs" - } - return "jsx" -} - -func (tx *JSXTransformer) getJsxFactoryCallee(isStaticChildren bool) *ast.Node { - t := tx.getJsxFactoryCalleePrimitive(isStaticChildren) - return tx.getImplicitImportForName(t) -} - -func (tx *JSXTransformer) getImplicitJsxFragmentReference() *ast.Node { - return tx.getImplicitImportForName("Fragment") -} - -func (tx *JSXTransformer) getImplicitImportForName(name string) *ast.Node { - importSource := tx.importSpecifier - if name != "createElement" { - importSource = ast.GetJSXRuntimeImport(importSource, tx.compilerOptions) - } - existing, ok := tx.utilizedImplicitRuntimeImports[importSource] - if ok { - elem, ok := existing[name] - if ok { - return elem.AsImportSpecifier().Name() - } - } else { - tx.utilizedImplicitRuntimeImports[importSource] = make(map[string]*ast.Node) - } - - generatedName := tx.Factory().NewUniqueNameEx("_"+name, printer.AutoGenerateOptions{ - Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsAllowNameSubstitution, - }) - specifier := tx.Factory().NewImportSpecifier(false, tx.Factory().NewIdentifier(name), generatedName) - tx.emitResolver.SetReferencedImportDeclaration(generatedName, specifier) - tx.utilizedImplicitRuntimeImports[importSource][name] = specifier - return specifier.Name() -} - -func (tx *JSXTransformer) setInChild(v bool) { - tx.inJsxChild = v -} - -func (tx *JSXTransformer) visit(node *ast.Node) *ast.Node { - if node == nil { - return nil - } - if node.SubtreeFacts()&ast.SubtreeContainsJsx == 0 { - return node - } - switch node.Kind { - case ast.KindSourceFile: - tx.setInChild(false) - return tx.visitSourceFile(node.AsSourceFile()) - case ast.KindJsxElement: - return tx.visitJsxElement(node.AsJsxElement()) - case ast.KindJsxSelfClosingElement: - return tx.visitJsxSelfClosingElement(node.AsJsxSelfClosingElement()) - case ast.KindJsxFragment: - return tx.visitJsxFragment(node.AsJsxFragment()) - case ast.KindJsxOpeningElement: - panic("JsxOpeningElement should not be visited, handled in visitJsxElement") - case ast.KindJsxOpeningFragment: - panic("JsxOpeningFragment should not be visited, handled in visitJsxFragment") - case ast.KindJsxText: - tx.setInChild(false) - return tx.visitJsxText(node.AsJsxText()) - case ast.KindJsxExpression: - tx.setInChild(false) - return tx.visitJsxExpression(node.AsJsxExpression()) - } - tx.setInChild(false) - return tx.Visitor().VisitEachChild(node) // by default, do nothing -} - -/** - * The react jsx/jsxs transform falls back to `createElement` when an explicit `key` argument comes after a spread - */ -func hasKeyAfterPropsSpread(node *ast.Node) bool { - spread := false - opener := node - if node.Kind == ast.KindJsxElement { - opener = node.AsJsxElement().OpeningElement - } // otherwise self-closing - for _, elem := range opener.Attributes().Properties() { - if ast.IsJsxSpreadAttribute(elem) && (!ast.IsObjectLiteralExpression(elem.Expression()) || core.Some(elem.Expression().Properties(), ast.IsSpreadAssignment)) { - spread = true - } else if spread && ast.IsJsxAttribute(elem) && ast.IsIdentifier(elem.Name()) && elem.Name().AsIdentifier().Text == "key" { - return true - } - } - return false -} - -func (tx *JSXTransformer) shouldUseCreateElement(node *ast.Node) bool { - return len(tx.importSpecifier) == 0 || hasKeyAfterPropsSpread(node) -} - -func insertStatementAfterPrologue[T any](to []*ast.Node, statement *ast.Node, isPrologueDirective func(callee T, node *ast.Node) bool, callee T) []*ast.Node { - if statement == nil { - return to - } - statementIdx := 0 - // skip all prologue directives to insert at the correct position - for ; statementIdx < len(to); statementIdx++ { - if !isPrologueDirective(callee, to[statementIdx]) { - break - } - } - return slices.Insert(to, statementIdx, statement) -} - -func (tx *JSXTransformer) isAnyPrologueDirective(node *ast.Node) bool { - return ast.IsPrologueDirective(node) || (tx.EmitContext().EmitFlags(node)&printer.EFCustomPrologue != 0) -} - -func (tx *JSXTransformer) insertStatementAfterCustomPrologue(to []*ast.Node, statement *ast.Node) []*ast.Node { - return insertStatementAfterPrologue(to, statement, (*JSXTransformer).isAnyPrologueDirective, tx) -} - -func sortByImportDeclarationSource(a *ast.Node, b *ast.Node) int { - return stringutil.CompareStringsCaseSensitive(a.AsImportDeclaration().ModuleSpecifier.AsStringLiteral().Text, b.AsImportDeclaration().ModuleSpecifier.AsStringLiteral().Text) -} - -func getSpecifierOfRequireCall(s *ast.Node) string { - return s.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0].AsStringLiteral().Text -} - -func sortByRequireSource(a *ast.Node, b *ast.Node) int { - return stringutil.CompareStringsCaseSensitive(getSpecifierOfRequireCall(a), getSpecifierOfRequireCall(b)) -} - -func sortImportSpecifiers(a *ast.Node, b *ast.Node) int { - res := stringutil.CompareStringsCaseSensitive(a.AsImportSpecifier().PropertyName.Text(), b.AsImportSpecifier().PropertyName.Text()) - if res != 0 { - return res - } - return stringutil.CompareStringsCaseSensitive(a.AsImportSpecifier().Name().AsIdentifier().Text, b.AsImportSpecifier().Name().AsIdentifier().Text) -} - -func getSortedSpecifiers(m map[string]*ast.Node) []*ast.Node { - res := slices.Collect(maps.Values(m)) - slices.SortFunc(res, sortImportSpecifiers) - return res -} - -func (tx *JSXTransformer) visitSourceFile(file *ast.SourceFile) *ast.Node { - if file.IsDeclarationFile { - return file.AsNode() - } - - tx.currentSourceFile = file - tx.importSpecifier = ast.GetJSXImplicitImportBase(tx.compilerOptions, file) - tx.filenameDeclaration = nil - tx.utilizedImplicitRuntimeImports = make(map[string]map[string]*ast.Node) - - visited := tx.Visitor().VisitEachChild(file.AsNode()) - tx.EmitContext().AddEmitHelper(visited.AsNode(), tx.EmitContext().ReadEmitHelpers()...) - statements := visited.Statements() - statementsUpdated := false - if tx.filenameDeclaration != nil { - statements = tx.insertStatementAfterCustomPrologue(statements, tx.Factory().NewVariableStatement(nil, tx.Factory().NewVariableDeclarationList( - ast.NodeFlagsConst, - tx.Factory().NewNodeList([]*ast.Node{tx.filenameDeclaration}), - ))) - statementsUpdated = true - } - - if len(tx.utilizedImplicitRuntimeImports) > 0 { - // A key difference from strada is that these imports are sorted in corsa, rather than appearing in a use-defined order - if ast.IsExternalModule(file) { - statementsUpdated = true - newStatements := make([]*ast.Node, 0, len(tx.utilizedImplicitRuntimeImports)) - for importSource, importSpecifiersMap := range tx.utilizedImplicitRuntimeImports { - s := tx.Factory().NewImportDeclaration( - nil, - tx.Factory().NewImportClause(ast.KindUnknown, nil, tx.Factory().NewNamedImports(tx.Factory().NewNodeList(getSortedSpecifiers(importSpecifiersMap)))), - tx.Factory().NewStringLiteral(importSource), - nil, - ) - ast.SetParentInChildren(s) - newStatements = append(newStatements, s) - - } - slices.SortFunc(newStatements, sortByImportDeclarationSource) - for _, e := range newStatements { - statements = tx.insertStatementAfterCustomPrologue(statements, e) - } - } else if ast.IsExternalOrCommonJSModule(file) { - statementsUpdated = true - newStatements := make([]*ast.Node, 0, len(tx.utilizedImplicitRuntimeImports)) - for importSource, importSpecifiersMap := range tx.utilizedImplicitRuntimeImports { - sorted := getSortedSpecifiers(importSpecifiersMap) - asBindingElems := make([]*ast.Node, 0, len(sorted)) - for _, elem := range sorted { - asBindingElems = append(asBindingElems, tx.Factory().NewBindingElement(nil, elem.AsImportSpecifier().PropertyName, elem.AsImportSpecifier().Name(), nil)) - } - s := tx.Factory().NewVariableStatement(nil, tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{tx.Factory().NewVariableDeclaration( - tx.Factory().NewBindingPattern(ast.KindObjectBindingPattern, tx.Factory().NewNodeList(asBindingElems)), - nil, - nil, - tx.Factory().NewCallExpression(tx.Factory().NewIdentifier("require"), nil, nil, tx.Factory().NewNodeList([]*ast.Node{tx.Factory().NewStringLiteral(importSource)}), ast.NodeFlagsNone), - )}))) - ast.SetParentInChildren(s) - newStatements = append(newStatements, s) - } - slices.SortFunc(newStatements, sortByRequireSource) - for _, e := range newStatements { - statements = tx.insertStatementAfterCustomPrologue(statements, e) - } - } else { - // Do nothing (script file) - consider an error in the checker? - } - } - - if statementsUpdated { - visited = tx.Factory().UpdateSourceFile(file, tx.Factory().NewNodeList(statements), file.EndOfFileToken) - } - - tx.currentSourceFile = nil - tx.importSpecifier = "" - tx.filenameDeclaration = nil - tx.utilizedImplicitRuntimeImports = nil - - return visited -} - -func (tx *JSXTransformer) visitJsxElement(element *ast.JsxElement) *ast.Node { - tagTransform := (*JSXTransformer).visitJsxOpeningLikeElementJSX - if tx.shouldUseCreateElement(element.AsNode()) { - tagTransform = (*JSXTransformer).visitJsxOpeningLikeElementCreateElement - } - return tagTransform(tx, element.OpeningElement, element.Children, element.AsNode()) -} - -func (tx *JSXTransformer) visitJsxSelfClosingElement(element *ast.JsxSelfClosingElement) *ast.Node { - tagTransform := (*JSXTransformer).visitJsxOpeningLikeElementJSX - if tx.shouldUseCreateElement(element.AsNode()) { - tagTransform = (*JSXTransformer).visitJsxOpeningLikeElementCreateElement - } - return tagTransform(tx, element.AsNode(), nil, element.AsNode()) -} - -func (tx *JSXTransformer) visitJsxFragment(fragment *ast.JsxFragment) *ast.Node { - tagTransform := (*JSXTransformer).visitJsxOpeningFragmentJSX - if len(tx.importSpecifier) == 0 { - tagTransform = (*JSXTransformer).visitJsxOpeningFragmentCreateElement - } - return tagTransform(tx, fragment.OpeningFragment.AsJsxOpeningFragment(), fragment.Children, fragment.AsNode()) -} - -func (tx *JSXTransformer) convertJsxChildrenToChildrenPropObject(children []*ast.JsxChild) *ast.Node { - prop := tx.convertJsxChildrenToChildrenPropAssignment(children) - if prop == nil { - return nil - } - return tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{prop}), false) -} - -func (tx *JSXTransformer) transformJsxChildToExpression(node *ast.Node) *ast.Node { - tx.setInChild(true) - defer tx.setInChild(false) - return tx.Visitor().Visit(node) -} - -func (tx *JSXTransformer) convertJsxChildrenToChildrenPropAssignment(children []*ast.JsxChild) *ast.Node { - nonWhitespceChildren := ast.GetSemanticJsxChildren(children) - if len(nonWhitespceChildren) == 1 && (nonWhitespceChildren[0].Kind != ast.KindJsxExpression || nonWhitespceChildren[0].AsJsxExpression().DotDotDotToken == nil) { - result := tx.transformJsxChildToExpression(nonWhitespceChildren[0]) - if result == nil { - return nil - } - return tx.Factory().NewPropertyAssignment(nil, tx.Factory().NewIdentifier("children"), nil, nil, result) - } - results := make([]*ast.Node, 0, len(nonWhitespceChildren)) - for _, child := range nonWhitespceChildren { - res := tx.transformJsxChildToExpression(child) - if res == nil { - continue - } - results = append(results, res) - } - if len(results) == 0 { - return nil - } - return tx.Factory().NewPropertyAssignment(nil, tx.Factory().NewIdentifier("children"), nil, nil, tx.Factory().NewArrayLiteralExpression(tx.Factory().NewNodeList(results), false)) -} - -func (tx *JSXTransformer) getTagName(node *ast.Node) *ast.Node { - if node.Kind == ast.KindJsxElement { - return tx.getTagName(node.AsJsxElement().OpeningElement) - } else if ast.IsJsxOpeningLikeElement(node) { - tagName := node.TagName() - if ast.IsIdentifier(tagName) && scanner.IsIntrinsicJsxName(tagName.Text()) { - return tx.Factory().NewStringLiteral(tagName.Text()) - } else if ast.IsJsxNamespacedName(tagName) { - return tx.Factory().NewStringLiteral(tagName.AsJsxNamespacedName().Namespace.Text() + ":" + tagName.AsJsxNamespacedName().Name().Text()) - } else { - return createExpressionFromEntityName(tx.Factory(), tagName) - } - } else { - panic("unhandled node kind passed to getTagName: " + node.Kind.String()) - } -} - -func (tx *JSXTransformer) visitJsxOpeningLikeElementJSX(element *ast.Node, children *ast.NodeList, location *ast.Node) *ast.Node { - tagName := tx.getTagName(element) - var childrenProp *ast.Node - if children != nil && len(children.Nodes) > 0 { - childrenProp = tx.convertJsxChildrenToChildrenPropAssignment(children.Nodes) - } - var keyAttr *ast.Node - attrs := element.Attributes().AsJsxAttributes().Properties.Nodes - for i, p := range attrs { - if p.Kind == ast.KindJsxAttribute && p.AsJsxAttribute().Name() != nil && ast.IsIdentifier(p.AsJsxAttribute().Name()) && p.AsJsxAttribute().Name().AsIdentifier().Text == "key" { - keyAttr = p - attrs = slices.Clone(attrs) - attrs = slices.Delete(attrs, i, i+1) - break - } - } - var object *ast.Node - if len(attrs) > 0 { - object = tx.transformJsxAttributesToObjectProps(attrs, childrenProp) - } else { - objectChildren := []*ast.Node{} - if childrenProp != nil { - objectChildren = append(objectChildren, childrenProp) - } - object = tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList(objectChildren), false) // When there are no attributes, React wants {} - } - return tx.visitJsxOpeningLikeElementOrFragmentJSX( - tagName, - object, - keyAttr, - children, - location, - ) -} - -func (tx *JSXTransformer) transformJsxAttributesToObjectProps(attrs []*ast.Node, childrenProp *ast.Node) *ast.Node { - target := tx.compilerOptions.GetEmitScriptTarget() - if target >= core.ScriptTargetES2018 { - // target has object spreads, can keep as-is - return tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList(tx.transformJsxAttributesToProps(attrs, childrenProp)), false) - } - return tx.transformJsxAttributesToExpression(attrs, childrenProp) -} - -func (tx *JSXTransformer) transformJsxAttributesToExpression(attrs []*ast.Node, childrenProp *ast.Node) *ast.Node { - expressions := make([]*ast.Expression, 0, 2) - properties := make([]*ast.ObjectLiteralElement, 0, len(attrs)) - - for _, attr := range attrs { - if ast.IsJsxSpreadAttribute(attr) { - // as an optimization we try to flatten the first level of spread inline object - // as if its props would be passed as JSX attributes - if ast.IsObjectLiteralExpression(attr.Expression()) && !hasProto(attr.Expression().AsObjectLiteralExpression()) { - for _, prop := range attr.Expression().Properties() { - if ast.IsSpreadAssignment(prop) { - expressions, properties = tx.combinePropertiesIntoNewExpression(expressions, properties) - expressions = append(expressions, tx.Visitor().Visit(prop.Expression())) - continue - } - properties = append(properties, tx.Visitor().Visit(prop)) - } - continue - } - expressions, properties = tx.combinePropertiesIntoNewExpression(expressions, properties) - expressions = append(expressions, tx.Visitor().Visit(attr.Expression())) - continue - } - properties = append(properties, tx.transformJsxAttributeToObjectLiteralElement(attr.AsJsxAttribute())) - } - - if childrenProp != nil { - properties = append(properties, childrenProp) - } - - expressions, _ = tx.combinePropertiesIntoNewExpression(expressions, properties) - - if len(expressions) > 0 && !ast.IsObjectLiteralExpression(expressions[0]) { - // We must always emit at least one object literal before a spread attribute - // as the JSX always factory expects a fresh object, so we need to make a copy here - // we also avoid mutating an external reference by doing this (first expression is used as assign's target) - expressions = append([]*ast.Expression{tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{}), false)}, expressions...) - } - - if len(expressions) == 1 { - return expressions[0] - } - return tx.Factory().NewAssignHelper(expressions, tx.compilerOptions.GetEmitScriptTarget()) -} - -func (tx *JSXTransformer) combinePropertiesIntoNewExpression(expressions []*ast.Expression, props []*ast.ObjectLiteralElement) ([]*ast.Expression, []*ast.ObjectLiteralElement) { - if len(props) == 0 { - return expressions, props - } - newObj := tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList(props), false) - expressions = append(expressions, newObj) - return expressions, nil -} - -func (tx *JSXTransformer) transformJsxAttributesToProps(attrs []*ast.Node, childrenProp *ast.Node) []*ast.Node { - props := make([]*ast.Node, 0, len(attrs)) - for _, attr := range attrs { - if attr.Kind == ast.KindJsxSpreadAttribute { - res := tx.transformJsxSpreadAttributesToProps(attr.AsJsxSpreadAttribute()) - props = append(props, res...) - } else { - props = append(props, tx.transformJsxAttributeToObjectLiteralElement(attr.AsJsxAttribute())) - } - } - if childrenProp != nil { - props = append(props, childrenProp) - } - return props -} - -func hasProto(obj *ast.ObjectLiteralExpression) bool { - for _, p := range obj.Properties.Nodes { - if ast.IsPropertyAssignment(p) && (ast.IsStringLiteral(p.Name()) || ast.IsIdentifier(p.Name())) && p.Name().Text() == "__proto__" { - return true - } - } - return false -} - -func (tx *JSXTransformer) transformJsxSpreadAttributesToProps(node *ast.JsxSpreadAttribute) []*ast.Node { - if ast.IsObjectLiteralExpression(node.Expression) && !hasProto(node.Expression.AsObjectLiteralExpression()) { - res, _ := tx.Visitor().VisitSlice(node.Expression.Properties()) - return res - } - return []*ast.Node{tx.Factory().NewSpreadAssignment(tx.Visitor().Visit(node.Expression))} -} - -func (tx *JSXTransformer) transformJsxAttributeToObjectLiteralElement(node *ast.JsxAttribute) *ast.Node { - name := tx.getAttributeName(node) - expression := tx.transformJsxAttributeInitializer(node.Initializer) - return tx.Factory().NewPropertyAssignment(nil, name, nil, nil, expression) -} - -/** -* Emit an attribute name, which is quoted if it needs to be quoted. Because -* these emit into an object literal property name, we don't need to be worried -* about keywords, just non-identifier characters - */ -func (tx *JSXTransformer) getAttributeName(node *ast.JsxAttribute) *ast.Node { - name := node.Name() - if ast.IsIdentifier(name) { - text := name.Text() - if scanner.IsIdentifierText(text, core.LanguageVariantStandard) { - return name - } - return tx.Factory().NewStringLiteral(text) - } - // must be jsx namespace - return tx.Factory().NewStringLiteral(name.AsJsxNamespacedName().Namespace.Text() + ":" + name.AsJsxNamespacedName().Name().Text()) -} - -func (tx *JSXTransformer) transformJsxAttributeInitializer(node *ast.Node) *ast.Node { - if node == nil { - return tx.Factory().NewTrueExpression() - } - if node.Kind == ast.KindStringLiteral { - // Always recreate the literal to escape any escape sequences or newlines which may be in the original jsx string and which - // Need to be escaped to be handled correctly in a normal string - res := tx.Factory().NewStringLiteral(decodeEntities(node.Text())) - res.Loc = node.Loc - return res - } - if node.Kind == ast.KindJsxExpression { - if node.AsJsxExpression().Expression == nil { - return tx.Factory().NewTrueExpression() - } - return tx.Visitor().Visit(node.AsJsxExpression().Expression) - } - if ast.IsJsxElement(node) || ast.IsJsxSelfClosingElement(node) || ast.IsJsxFragment(node) { - tx.setInChild(false) - return tx.Visitor().Visit(node) - } - panic("Unhandled node kind found in jsx initializer: " + node.Kind.String()) -} - -func (tx *JSXTransformer) visitJsxOpeningLikeElementOrFragmentJSX( - tagName *ast.Expression, - object *ast.Expression, - keyAttr *ast.Node, - children *ast.NodeList, - location *ast.Node, -) *ast.Node { - var nonWhitespaceChildren []*ast.Node - if children != nil { - nonWhitespaceChildren = ast.GetSemanticJsxChildren(children.Nodes) - } - isStaticChildren := len(nonWhitespaceChildren) > 1 || (len(nonWhitespaceChildren) == 1 && ast.IsJsxExpression(nonWhitespaceChildren[0]) && nonWhitespaceChildren[0].AsJsxExpression().DotDotDotToken != nil) - args := make([]*ast.Node, 0, 3) - args = append(args, tagName, object) - // function jsx(type, config, maybeKey) {} - // "maybeKey" is optional. It is acceptable to use "_jsx" without a third argument - if keyAttr != nil { - args = append(args, tx.transformJsxAttributeInitializer(keyAttr.Initializer())) - } - - if tx.compilerOptions.Jsx == core.JsxEmitReactJSXDev { - originalFile := tx.EmitContext().Original(tx.currentSourceFile.AsNode()) - if originalFile != nil && ast.IsSourceFile(originalFile) { - // "maybeKey" has to be replaced with "void 0" to not break the jsxDEV signature - if keyAttr == nil { - args = append(args, tx.Factory().NewVoidZeroExpression()) - } - // isStaticChildren development flag - if isStaticChildren { - args = append(args, tx.Factory().NewTrueExpression()) - } else { - args = append(args, tx.Factory().NewFalseExpression()) - } - // __source development flag - line, col := scanner.GetECMALineAndCharacterOfPosition(originalFile.AsSourceFile(), location.Pos()) - args = append(args, tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewPropertyAssignment(nil, tx.Factory().NewIdentifier("fileName"), nil, nil, tx.getCurrentFileNameExpression()), - tx.Factory().NewPropertyAssignment(nil, tx.Factory().NewIdentifier("lineNumber"), nil, nil, tx.Factory().NewNumericLiteral(strconv.FormatInt(int64(line+1), 10))), - tx.Factory().NewPropertyAssignment(nil, tx.Factory().NewIdentifier("columnNumber"), nil, nil, tx.Factory().NewNumericLiteral(strconv.FormatInt(int64(col+1), 10))), - }), false)) - // __self development flag - args = append(args, tx.Factory().NewThisExpression()) - } - } - - element := tx.Factory().NewCallExpression(tx.getJsxFactoryCallee(isStaticChildren), nil, nil, tx.Factory().NewNodeList(args), ast.NodeFlagsNone) - element.Loc = location.Loc - - if tx.inJsxChild { - tx.EmitContext().AddEmitFlags(element, printer.EFStartOnNewLine) - } - - return element -} - -func (tx *JSXTransformer) visitJsxOpeningFragmentJSX(fragment *ast.JsxOpeningFragment, children *ast.NodeList, location *ast.Node) *ast.Node { - var childrenProps *ast.Expression - if children != nil && len(children.Nodes) > 0 { - result := tx.convertJsxChildrenToChildrenPropObject(children.Nodes) - if result != nil { - childrenProps = result - } - } - if childrenProps == nil { - childrenProps = tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{}), false) - } - return tx.visitJsxOpeningLikeElementOrFragmentJSX( - tx.getImplicitJsxFragmentReference(), - childrenProps, - nil, - children, - location, - ) -} - -func (tx *JSXTransformer) createReactNamespace(reactNamespace string, parent *ast.Node) *ast.Node { - // To ensure the emit resolver can properly resolve the namespace, we need to - // treat this identifier as if it were a source tree node by clearing the `Synthesized` - // flag and setting a parent node. TODO: Is this still true? The emit resolver is supposed to be - // hardened aginast this, so long as the node retains original node pointers back to a parsed node - if len(reactNamespace) == 0 { - reactNamespace = "React" - } - react := tx.Factory().NewIdentifier(reactNamespace) - react.Flags &= ^ast.NodeFlagsSynthesized - - // Set the parent that is in parse tree - // this makes sure that parent chain is intact for checker to traverse complete scope tree - react.Parent = tx.EmitContext().ParseNode(parent) - return react -} - -func (tx *JSXTransformer) createJsxFactoryExpressionFromEntityName(e *ast.Node, parent *ast.Node) *ast.Node { - if ast.IsQualifiedName(e) { - left := tx.createJsxFactoryExpressionFromEntityName(e.AsQualifiedName().Left, parent) - right := tx.Factory().NewIdentifier(e.AsQualifiedName().Right.Text()) - return tx.Factory().NewPropertyAccessExpression(left, nil, right, ast.NodeFlagsNone) - } - return tx.createReactNamespace(e.AsIdentifier().Text, parent) -} - -func (tx *JSXTransformer) createJsxPsuedoFactoryExpression(parent *ast.Node, e *ast.Node, target string) *ast.Node { - if e != nil { - return tx.createJsxFactoryExpressionFromEntityName(e, parent) - } - return tx.Factory().NewPropertyAccessExpression( - tx.createReactNamespace(tx.compilerOptions.ReactNamespace, parent), - nil, - tx.Factory().NewIdentifier(target), - ast.NodeFlagsNone, - ) -} - -func (tx *JSXTransformer) createJsxFactoryExpression(parent *ast.Node) *ast.Node { - e := tx.emitResolver.GetJsxFactoryEntity(tx.currentSourceFile.AsNode()) - return tx.createJsxPsuedoFactoryExpression(parent, e, "createElement") -} - -func (tx *JSXTransformer) createJsxFragmentFactoryExpression(parent *ast.Node) *ast.Node { - e := tx.emitResolver.GetJsxFragmentFactoryEntity(tx.currentSourceFile.AsNode()) - return tx.createJsxPsuedoFactoryExpression(parent, e, "Fragment") -} - -func (tx *JSXTransformer) visitJsxOpeningLikeElementCreateElement(element *ast.Node, children *ast.NodeList, location *ast.Node) *ast.Node { - tagName := tx.getTagName(element) - attrs := element.Attributes().Properties() - var objectProperties *ast.Expression - if len(attrs) > 0 { - objectProperties = tx.transformJsxAttributesToObjectProps(attrs, nil) - } else { - objectProperties = tx.Factory().NewKeywordExpression(ast.KindNullKeyword) // When there are no attributes, React wants "null" - } - - var callee *ast.Expression - if len(tx.importSpecifier) == 0 { - callee = tx.createJsxFactoryExpression(element) - } else { - callee = tx.getImplicitImportForName("createElement") - } - - var newChildren []*ast.Node - if children != nil && len(children.Nodes) > 0 { - for _, c := range children.Nodes { - res := tx.transformJsxChildToExpression(c) - if res != nil { - if len(children.Nodes) > 1 { - tx.EmitContext().AddEmitFlags(res, printer.EFStartOnNewLine) - } - newChildren = append(newChildren, res) - } - } - } - - args := make([]*ast.Expression, 0, len(newChildren)+2) - args = append(args, tagName) - args = append(args, objectProperties) - args = append(args, newChildren...) - - result := tx.Factory().NewCallExpression( - callee, - nil, - nil, - tx.Factory().NewNodeList(args), - ast.NodeFlagsNone, - ) - result.Loc = location.Loc - - if tx.inJsxChild { - tx.EmitContext().AddEmitFlags(result, printer.EFStartOnNewLine) - } - return result -} - -func (tx *JSXTransformer) visitJsxOpeningFragmentCreateElement(fragment *ast.JsxOpeningFragment, children *ast.NodeList, location *ast.Node) *ast.Node { - tagName := tx.createJsxFragmentFactoryExpression(fragment.AsNode()) - callee := tx.createJsxFactoryExpression(fragment.AsNode()) - - var newChildren []*ast.Node - if children != nil && len(children.Nodes) > 0 { - for _, c := range children.Nodes { - res := tx.transformJsxChildToExpression(c) - if res != nil { - if len(children.Nodes) > 1 { - tx.EmitContext().AddEmitFlags(res, printer.EFStartOnNewLine) - } - newChildren = append(newChildren, res) - } - } - } - - args := make([]*ast.Expression, 0, len(newChildren)+2) - args = append(args, tagName) - args = append(args, tx.Factory().NewKeywordExpression(ast.KindNullKeyword)) - args = append(args, newChildren...) - - result := tx.Factory().NewCallExpression( - callee, - nil, - nil, - tx.Factory().NewNodeList(args), - ast.NodeFlagsNone, - ) - result.Loc = location.Loc - - if tx.inJsxChild { - tx.EmitContext().AddEmitFlags(result, printer.EFStartOnNewLine) - } - return result -} - -func (tx *JSXTransformer) visitJsxText(text *ast.JsxText) *ast.Node { - fixed := fixupWhitespaceAndDecodeEntities(text.Text) - if len(fixed) == 0 { - return nil - } - return tx.Factory().NewStringLiteral(fixed) -} - -func addLineOfJsxText(b *strings.Builder, trimmedLine string, isInitial bool) { - // We do not escape the string here as that is handled by the printer - // when it emits the literal. We do, however, need to decode JSX entities. - decoded := decodeEntities(trimmedLine) - if !isInitial { - b.WriteString(" ") - } - b.WriteString(decoded) -} - -/** -* JSX trims whitespace at the end and beginning of lines, except that the -* start/end of a tag is considered a start/end of a line only if that line is -* on the same line as the closing tag. See examples in -* tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx -* See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model -* -* An equivalent algorithm would be: -* - If there is only one line, return it. -* - If there is only whitespace (but multiple lines), return `undefined`. -* - Split the text into lines. -* - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines. -* - Decode entities on each line (individually). -* - Remove empty lines and join the rest with " ". - */ -func fixupWhitespaceAndDecodeEntities(text string) string { - acc := &strings.Builder{} - initial := true - // First non-whitespace character on this line. - firstNonWhitespace := 0 - // End byte position of the last non-whitespace character on this line. - lastNonWhitespaceEnd := -1 - // These initial values are special because the first line is: - // firstNonWhitespace = 0 to indicate that we want leading whitespace, - // but lastNonWhitespaceEnd = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace. - for i := 0; i < len(text); i++ { - c, size := utf8.DecodeRuneInString(text[i:]) - if stringutil.IsLineBreak(c) { - // If we've seen any non-whitespace characters on this line, add the 'trim' of the line. - // (lastNonWhitespaceEnd === -1 is a special flag to detect whether the first line is all whitespace.) - if firstNonWhitespace != -1 && lastNonWhitespaceEnd != -1 { - addLineOfJsxText(acc, text[firstNonWhitespace:lastNonWhitespaceEnd+1], initial) - initial = false - } - - // Reset firstNonWhitespace for the next line. - // Don't bother to reset lastNonWhitespaceEnd because we ignore it if firstNonWhitespace = -1. - firstNonWhitespace = -1 - } else if !stringutil.IsWhiteSpaceSingleLine(c) { - lastNonWhitespaceEnd = i + size - 1 // Store the end byte position of the character - if firstNonWhitespace == -1 { - firstNonWhitespace = i - } - } - - if size > 1 { - i += (size - 1) - } - } - - if firstNonWhitespace != -1 { - // Last line had a non-whitespace character. Emit the 'trimLeft', meaning keep trailing whitespace. - addLineOfJsxText(acc, text[firstNonWhitespace:], initial) - } - return acc.String() -} - -func (tx *JSXTransformer) visitJsxExpression(expression *ast.JsxExpression) *ast.Node { - e := tx.Visitor().Visit(expression.Expression) - if expression.DotDotDotToken != nil { - return tx.Factory().NewSpreadElement(e) - } - return e -} - -var htmlEntityMatcher = regexp2.MustCompile(`&((#((\d+)|x([\da-fA-F]+)))|(\w+));`, regexp2.ECMAScript) - -func htmlEntityReplacer(m regexp2.Match) string { - decimal := m.GroupByNumber(4) - if decimal != nil && decimal.Capture.String() != "" { - parsed, err := strconv.ParseInt(decimal.Capture.String(), 10, 32) - if err == nil { - return string(rune(parsed)) - } - } - hex := m.GroupByNumber(5) - if hex != nil && hex.Capture.String() != "" { - parsed, err := strconv.ParseInt(hex.Capture.String(), 16, 32) - if err == nil { - return string(rune(parsed)) - } - } - word := m.GroupByNumber(6) - if word != nil && word.Capture.String() != "" { - res, ok := entities[word.Capture.String()] - if ok { - return string(res) - } - } - return m.String() -} - -/** -* Replace entities like " ", "{", and "�" with the characters they encode. -* See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references - */ -func decodeEntities(text string) string { - res, err := htmlEntityMatcher.ReplaceFunc(text, htmlEntityReplacer, -1, -1) - if err != nil { - panic(err.Error()) - } - return res -} - -var entities = map[string]rune{ - "quot": 0x0022, - "amp": 0x0026, - "apos": 0x0027, - "lt": 0x003C, - "gt": 0x003E, - "nbsp": 0x00A0, - "iexcl": 0x00A1, - "cent": 0x00A2, - "pound": 0x00A3, - "curren": 0x00A4, - "yen": 0x00A5, - "brvbar": 0x00A6, - "sect": 0x00A7, - "uml": 0x00A8, - "copy": 0x00A9, - "ordf": 0x00AA, - "laquo": 0x00AB, - "not": 0x00AC, - "shy": 0x00AD, - "reg": 0x00AE, - "macr": 0x00AF, - "deg": 0x00B0, - "plusmn": 0x00B1, - "sup2": 0x00B2, - "sup3": 0x00B3, - "acute": 0x00B4, - "micro": 0x00B5, - "para": 0x00B6, - "middot": 0x00B7, - "cedil": 0x00B8, - "sup1": 0x00B9, - "ordm": 0x00BA, - "raquo": 0x00BB, - "frac14": 0x00BC, - "frac12": 0x00BD, - "frac34": 0x00BE, - "iquest": 0x00BF, - "Agrave": 0x00C0, - "Aacute": 0x00C1, - "Acirc": 0x00C2, - "Atilde": 0x00C3, - "Auml": 0x00C4, - "Aring": 0x00C5, - "AElig": 0x00C6, - "Ccedil": 0x00C7, - "Egrave": 0x00C8, - "Eacute": 0x00C9, - "Ecirc": 0x00CA, - "Euml": 0x00CB, - "Igrave": 0x00CC, - "Iacute": 0x00CD, - "Icirc": 0x00CE, - "Iuml": 0x00CF, - "ETH": 0x00D0, - "Ntilde": 0x00D1, - "Ograve": 0x00D2, - "Oacute": 0x00D3, - "Ocirc": 0x00D4, - "Otilde": 0x00D5, - "Ouml": 0x00D6, - "times": 0x00D7, - "Oslash": 0x00D8, - "Ugrave": 0x00D9, - "Uacute": 0x00DA, - "Ucirc": 0x00DB, - "Uuml": 0x00DC, - "Yacute": 0x00DD, - "THORN": 0x00DE, - "szlig": 0x00DF, - "agrave": 0x00E0, - "aacute": 0x00E1, - "acirc": 0x00E2, - "atilde": 0x00E3, - "auml": 0x00E4, - "aring": 0x00E5, - "aelig": 0x00E6, - "ccedil": 0x00E7, - "egrave": 0x00E8, - "eacute": 0x00E9, - "ecirc": 0x00EA, - "euml": 0x00EB, - "igrave": 0x00EC, - "iacute": 0x00ED, - "icirc": 0x00EE, - "iuml": 0x00EF, - "eth": 0x00F0, - "ntilde": 0x00F1, - "ograve": 0x00F2, - "oacute": 0x00F3, - "ocirc": 0x00F4, - "otilde": 0x00F5, - "ouml": 0x00F6, - "divide": 0x00F7, - "oslash": 0x00F8, - "ugrave": 0x00F9, - "uacute": 0x00FA, - "ucirc": 0x00FB, - "uuml": 0x00FC, - "yacute": 0x00FD, - "thorn": 0x00FE, - "yuml": 0x00FF, - "OElig": 0x0152, - "oelig": 0x0153, - "Scaron": 0x0160, - "scaron": 0x0161, - "Yuml": 0x0178, - "fnof": 0x0192, - "circ": 0x02C6, - "tilde": 0x02DC, - "Alpha": 0x0391, - "Beta": 0x0392, - "Gamma": 0x0393, - "Delta": 0x0394, - "Epsilon": 0x0395, - "Zeta": 0x0396, - "Eta": 0x0397, - "Theta": 0x0398, - "Iota": 0x0399, - "Kappa": 0x039A, - "Lambda": 0x039B, - "Mu": 0x039C, - "Nu": 0x039D, - "Xi": 0x039E, - "Omicron": 0x039F, - "Pi": 0x03A0, - "Rho": 0x03A1, - "Sigma": 0x03A3, - "Tau": 0x03A4, - "Upsilon": 0x03A5, - "Phi": 0x03A6, - "Chi": 0x03A7, - "Psi": 0x03A8, - "Omega": 0x03A9, - "alpha": 0x03B1, - "beta": 0x03B2, - "gamma": 0x03B3, - "delta": 0x03B4, - "epsilon": 0x03B5, - "zeta": 0x03B6, - "eta": 0x03B7, - "theta": 0x03B8, - "iota": 0x03B9, - "kappa": 0x03BA, - "lambda": 0x03BB, - "mu": 0x03BC, - "nu": 0x03BD, - "xi": 0x03BE, - "omicron": 0x03BF, - "pi": 0x03C0, - "rho": 0x03C1, - "sigmaf": 0x03C2, - "sigma": 0x03C3, - "tau": 0x03C4, - "upsilon": 0x03C5, - "phi": 0x03C6, - "chi": 0x03C7, - "psi": 0x03C8, - "omega": 0x03C9, - "thetasym": 0x03D1, - "upsih": 0x03D2, - "piv": 0x03D6, - "ensp": 0x2002, - "emsp": 0x2003, - "thinsp": 0x2009, - "zwnj": 0x200C, - "zwj": 0x200D, - "lrm": 0x200E, - "rlm": 0x200F, - "ndash": 0x2013, - "mdash": 0x2014, - "lsquo": 0x2018, - "rsquo": 0x2019, - "sbquo": 0x201A, - "ldquo": 0x201C, - "rdquo": 0x201D, - "bdquo": 0x201E, - "dagger": 0x2020, - "Dagger": 0x2021, - "bull": 0x2022, - "hellip": 0x2026, - "permil": 0x2030, - "prime": 0x2032, - "Prime": 0x2033, - "lsaquo": 0x2039, - "rsaquo": 0x203A, - "oline": 0x203E, - "frasl": 0x2044, - "euro": 0x20AC, - "image": 0x2111, - "weierp": 0x2118, - "real": 0x211C, - "trade": 0x2122, - "alefsym": 0x2135, - "larr": 0x2190, - "uarr": 0x2191, - "rarr": 0x2192, - "darr": 0x2193, - "harr": 0x2194, - "crarr": 0x21B5, - "lArr": 0x21D0, - "uArr": 0x21D1, - "rArr": 0x21D2, - "dArr": 0x21D3, - "hArr": 0x21D4, - "forall": 0x2200, - "part": 0x2202, - "exist": 0x2203, - "empty": 0x2205, - "nabla": 0x2207, - "isin": 0x2208, - "notin": 0x2209, - "ni": 0x220B, - "prod": 0x220F, - "sum": 0x2211, - "minus": 0x2212, - "lowast": 0x2217, - "radic": 0x221A, - "prop": 0x221D, - "infin": 0x221E, - "ang": 0x2220, - "and": 0x2227, - "or": 0x2228, - "cap": 0x2229, - "cup": 0x222A, - "int": 0x222B, - "there4": 0x2234, - "sim": 0x223C, - "cong": 0x2245, - "asymp": 0x2248, - "ne": 0x2260, - "equiv": 0x2261, - "le": 0x2264, - "ge": 0x2265, - "sub": 0x2282, - "sup": 0x2283, - "nsub": 0x2284, - "sube": 0x2286, - "supe": 0x2287, - "oplus": 0x2295, - "otimes": 0x2297, - "perp": 0x22A5, - "sdot": 0x22C5, - "lceil": 0x2308, - "rceil": 0x2309, - "lfloor": 0x230A, - "rfloor": 0x230B, - "lang": 0x2329, - "rang": 0x232A, - "loz": 0x25CA, - "spades": 0x2660, - "clubs": 0x2663, - "hearts": 0x2665, - "diams": 0x2666, -} diff --git a/kitcom/internal/tsgo/transformers/jsxtransforms/utilities.go b/kitcom/internal/tsgo/transformers/jsxtransforms/utilities.go deleted file mode 100644 index 7f6df3f..0000000 --- a/kitcom/internal/tsgo/transformers/jsxtransforms/utilities.go +++ /dev/null @@ -1,20 +0,0 @@ -package jsxtransforms - -import "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - -func createExpressionFromEntityName(factory ast.NodeFactoryCoercible, node *ast.Node) *ast.Expression { - if ast.IsQualifiedName(node) { - left := createExpressionFromEntityName(factory, node.AsQualifiedName().Left) - // TODO(rbuckton): Does this need to be parented? - right := node.AsQualifiedName().Right.Clone(factory.AsNodeFactory()) - right.Loc = node.AsQualifiedName().Right.Loc - right.Parent = node.AsQualifiedName().Right.Parent - return factory.AsNodeFactory().NewPropertyAccessExpression(left, nil, right, ast.NodeFlagsNone) - } else { - // TODO(rbuckton): Does this need to be parented? - res := node.Clone(factory.AsNodeFactory()) - res.Loc = node.Loc - res.Parent = node.Parent - return res - } -} diff --git a/kitcom/internal/tsgo/transformers/modifiervisitor.go b/kitcom/internal/tsgo/transformers/modifiervisitor.go deleted file mode 100644 index 2b9a88f..0000000 --- a/kitcom/internal/tsgo/transformers/modifiervisitor.go +++ /dev/null @@ -1,28 +0,0 @@ -package transformers - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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) -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/commonjsmodule.go b/kitcom/internal/tsgo/transformers/moduletransforms/commonjsmodule.go deleted file mode 100644 index beab033..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/commonjsmodule.go +++ /dev/null @@ -1,2010 +0,0 @@ -package moduletransforms - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type CommonJSModuleTransformer struct { - transformers.Transformer - topLevelVisitor *ast.NodeVisitor // visits statements at top level of a module - topLevelNestedVisitor *ast.NodeVisitor // visits nested statements at top level of a module - discardedValueVisitor *ast.NodeVisitor // visits expressions whose values would be discarded at runtime - assignmentPatternVisitor *ast.NodeVisitor // visits assignment patterns in a destructuring assignment - compilerOptions *core.CompilerOptions - resolver binder.ReferenceResolver - getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind - moduleKind core.ModuleKind - languageVersion core.ScriptTarget - currentSourceFile *ast.SourceFile - currentModuleInfo *externalModuleInfo - parentNode *ast.Node // used for ancestor tracking via pushNode/popNode to detect expression identifiers - currentNode *ast.Node // used for ancestor tracking via pushNode/popNode to detect expression identifiers -} - -func NewCommonJSModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - compilerOptions := opts.CompilerOptions - emitContext := opts.Context - resolver := opts.Resolver - if resolver == nil { - resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) - } - tx := &CommonJSModuleTransformer{compilerOptions: compilerOptions, resolver: resolver, getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile} - tx.topLevelVisitor = emitContext.NewNodeVisitor(tx.visitTopLevel) - tx.topLevelNestedVisitor = emitContext.NewNodeVisitor(tx.visitTopLevelNested) - tx.discardedValueVisitor = emitContext.NewNodeVisitor(tx.visitDiscardedValue) - tx.assignmentPatternVisitor = emitContext.NewNodeVisitor(tx.visitAssignmentPattern) - tx.languageVersion = compilerOptions.GetEmitScriptTarget() - tx.moduleKind = compilerOptions.GetEmitModuleKind() - 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 *CommonJSModuleTransformer) 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 *CommonJSModuleTransformer) popNode(grandparentNode *ast.Node) { - tx.currentNode = tx.parentNode - tx.parentNode = grandparentNode -} - -// Visits a node at the top level of the source file. -func (tx *CommonJSModuleTransformer) visitTopLevel(node *ast.Node) *ast.Node { - grandparentNode := tx.pushNode(node) - defer tx.popNode(grandparentNode) - - switch node.Kind { - case ast.KindImportDeclaration: - node = tx.visitTopLevelImportDeclaration(node.AsImportDeclaration()) - case ast.KindImportEqualsDeclaration: - node = tx.visitTopLevelImportEqualsDeclaration(node.AsImportEqualsDeclaration()) - case ast.KindExportDeclaration: - node = tx.visitTopLevelExportDeclaration(node.AsExportDeclaration()) - case ast.KindExportAssignment: - node = tx.visitTopLevelExportAssignment(node.AsExportAssignment()) - case ast.KindFunctionDeclaration: - node = tx.visitTopLevelFunctionDeclaration(node.AsFunctionDeclaration()) - case ast.KindClassDeclaration: - node = tx.visitTopLevelClassDeclaration(node.AsClassDeclaration()) - case ast.KindVariableStatement: - node = tx.visitTopLevelVariableStatement(node.AsVariableStatement()) - default: - node = tx.visitTopLevelNestedNoStack(node) - } - return node -} - -// Visits nested elements at the top-level of a module. -func (tx *CommonJSModuleTransformer) visitTopLevelNested(node *ast.Node) *ast.Node { - grandparentNode := tx.pushNode(node) - defer tx.popNode(grandparentNode) - - return tx.visitTopLevelNestedNoStack(node) -} - -// Visits nested elements at the top-level of a module without ancestor tracking. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedNoStack(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindVariableStatement: - node = tx.visitTopLevelNestedVariableStatement(node.AsVariableStatement()) - case ast.KindForStatement: - node = tx.visitTopLevelNestedForStatement(node.AsForStatement()) - case ast.KindForInStatement, ast.KindForOfStatement: - node = tx.visitTopLevelNestedForInOrOfStatement(node.AsForInOrOfStatement()) - case ast.KindDoStatement: - node = tx.visitTopLevelNestedDoStatement(node.AsDoStatement()) - case ast.KindWhileStatement: - node = tx.visitTopLevelNestedWhileStatement(node.AsWhileStatement()) - case ast.KindLabeledStatement: - node = tx.visitTopLevelNestedLabeledStatement(node.AsLabeledStatement()) - case ast.KindWithStatement: - node = tx.visitTopLevelNestedWithStatement(node.AsWithStatement()) - case ast.KindIfStatement: - node = tx.visitTopLevelNestedIfStatement(node.AsIfStatement()) - case ast.KindSwitchStatement: - node = tx.visitTopLevelNestedSwitchStatement(node.AsSwitchStatement()) - case ast.KindCaseBlock: - node = tx.visitTopLevelNestedCaseBlock(node.AsCaseBlock()) - case ast.KindCaseClause, ast.KindDefaultClause: - node = tx.visitTopLevelNestedCaseOrDefaultClause(node.AsCaseOrDefaultClause()) - case ast.KindTryStatement: - node = tx.visitTopLevelNestedTryStatement(node.AsTryStatement()) - case ast.KindCatchClause: - node = tx.visitTopLevelNestedCatchClause(node.AsCatchClause()) - case ast.KindBlock: - node = tx.visitTopLevelNestedBlock(node.AsBlock()) - default: - node = tx.visitNoStack(node, false /*resultIsDiscarded*/) - } - return node -} - -// Visits source elements that are not top-level or top-level nested statements. -func (tx *CommonJSModuleTransformer) visit(node *ast.Node) *ast.Node { - grandparentNode := tx.pushNode(node) - defer tx.popNode(grandparentNode) - - return tx.visitNoStack(node, false /*resultIsDiscarded*/) -} - -// Visits source elements that are not top-level or top-level nested statements without ancestor tracking. -func (tx *CommonJSModuleTransformer) visitNoStack(node *ast.Node, resultIsDiscarded bool) *ast.Node { - // This visitor does not need to descend into the tree if there are no dynamic imports or identifiers in the subtree - if !ast.IsSourceFile(node) && node.SubtreeFacts()&(ast.SubtreeContainsDynamicImport|ast.SubtreeContainsIdentifier) == 0 { - return node - } - - switch node.Kind { - case ast.KindSourceFile: - node = tx.visitSourceFile(node.AsSourceFile()) - case ast.KindForStatement: - node = tx.visitForStatement(node.AsForStatement()) - case ast.KindForInStatement, ast.KindForOfStatement: - node = tx.visitForInOrOfStatement(node.AsForInOrOfStatement()) - case ast.KindExpressionStatement: - node = tx.visitExpressionStatement(node.AsExpressionStatement()) - case ast.KindVoidExpression: - node = tx.visitVoidExpression(node.AsVoidExpression()) - case ast.KindParenthesizedExpression: - node = tx.visitParenthesizedExpression(node.AsParenthesizedExpression(), resultIsDiscarded) - case ast.KindPartiallyEmittedExpression: - node = tx.visitPartiallyEmittedExpression(node.AsPartiallyEmittedExpression(), resultIsDiscarded) - case ast.KindCallExpression: - node = tx.visitCallExpression(node.AsCallExpression()) - case ast.KindTaggedTemplateExpression: - node = tx.visitTaggedTemplateExpression(node.AsTaggedTemplateExpression()) - case ast.KindBinaryExpression: - node = tx.visitBinaryExpression(node.AsBinaryExpression(), resultIsDiscarded) - case ast.KindPrefixUnaryExpression: - node = tx.visitPrefixUnaryExpression(node.AsPrefixUnaryExpression(), resultIsDiscarded) - case ast.KindPostfixUnaryExpression: - node = tx.visitPostfixUnaryExpression(node.AsPostfixUnaryExpression(), resultIsDiscarded) - case ast.KindShorthandPropertyAssignment: - node = tx.visitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment()) - case ast.KindIdentifier: - node = tx.visitIdentifier(node) - default: - node = tx.Visitor().VisitEachChild(node) - } - - return node -} - -// Visits source elements whose value is discarded if they are expressions. -func (tx *CommonJSModuleTransformer) visitDiscardedValue(node *ast.Node) *ast.Node { - grandparentNode := tx.pushNode(node) - defer tx.popNode(grandparentNode) - - return tx.visitNoStack(node, true /*resultIsDiscarded*/) -} - -func (tx *CommonJSModuleTransformer) visitAssignmentPattern(node *ast.Node) *ast.Node { - grandparentNode := tx.pushNode(node) - defer tx.popNode(grandparentNode) - - return tx.visitAssignmentPatternNoStack(node) -} - -func (tx *CommonJSModuleTransformer) visitAssignmentPatternNoStack(node *ast.Node) *ast.Node { - switch node.Kind { - // AssignmentPattern - case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression: - node = tx.assignmentPatternVisitor.VisitEachChild(node) - - // AssignmentProperty - case ast.KindPropertyAssignment: - node = tx.visitAssignmentProperty(node.AsPropertyAssignment()) - case ast.KindShorthandPropertyAssignment: - node = tx.visitShorthandAssignmentProperty(node.AsShorthandPropertyAssignment()) - - // AssignmentRestProperty - case ast.KindSpreadAssignment: - node = tx.visitAssignmentRestProperty(node.AsSpreadAssignment()) - - // AssignmentRestElement - case ast.KindSpreadElement: - node = tx.visitAssignmentRestElement(node.AsSpreadElement()) - - // AssignmentElement - default: - if ast.IsExpression(node) { - node = tx.visitAssignmentElement(node) - break - } - - node = tx.visitNoStack(node, false /*resultIsDiscarded*/) - } - return node -} - -func (tx *CommonJSModuleTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node { - if node.IsDeclarationFile || - !(ast.IsEffectiveExternalModule(node, tx.compilerOptions) || - node.SubtreeFacts()&ast.SubtreeContainsDynamicImport != 0) { - return node.AsNode() - } - - tx.currentSourceFile = node - tx.currentModuleInfo = collectExternalModuleInfo(node, tx.compilerOptions, tx.EmitContext(), tx.resolver) - updated := tx.transformCommonJSModule(node) - tx.currentSourceFile = nil - tx.currentModuleInfo = nil - return updated -} - -func (tx *CommonJSModuleTransformer) shouldEmitUnderscoreUnderscoreESModule() bool { - if tspath.FileExtensionIsOneOf(tx.currentSourceFile.FileName(), tspath.SupportedJSExtensionsFlat) && - tx.currentSourceFile.CommonJSModuleIndicator != nil && - (tx.currentSourceFile.ExternalModuleIndicator == nil /*|| tx.currentSourceFile.ExternalModuleIndicator == true*/) { // !!! - return false - } - if tx.currentModuleInfo.exportEquals == nil && ast.IsExternalModule(tx.currentSourceFile) { - return true - } - return false -} - -func (tx *CommonJSModuleTransformer) createUnderscoreUnderscoreESModule() *ast.Statement { - statement := tx.Factory().NewExpressionStatement( - tx.Factory().NewCallExpression( - tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("Object"), - nil, /*questionDotToken*/ - tx.Factory().NewIdentifier("defineProperty"), - ast.NodeFlagsNone, - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewIdentifier("exports"), - tx.Factory().NewStringLiteral("__esModule"), - tx.Factory().NewObjectLiteralExpression( - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewPropertyAssignment( - nil, /*modifiers*/ - tx.Factory().NewIdentifier("value"), - nil, /*postfixToken*/ - nil, /*typeNode*/ - tx.Factory().NewTrueExpression(), - ), - }), - false, /*multiLine*/ - ), - }), - ast.NodeFlagsNone, - ), - ) - tx.EmitContext().SetEmitFlags(statement, printer.EFCustomPrologue) - return statement -} - -func (tx *CommonJSModuleTransformer) transformCommonJSModule(node *ast.SourceFile) *ast.Node { - tx.EmitContext().StartVariableEnvironment() - - // emit standard prologue directives (e.g. "use strict") - prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes) - statements := slices.Clone(prologue) - - // ensure "use strict" if not present - if ast.IsExternalModule(tx.currentSourceFile) || - tx.compilerOptions.AlwaysStrict.DefaultIfUnknown(tx.compilerOptions.Strict).IsTrue() { - statements = tx.Factory().EnsureUseStrict(statements) - } - - // emit custom prologues from other transformations - custom, rest := tx.Factory().SplitCustomPrologue(rest) - statements = append(statements, core.FirstResult(tx.topLevelVisitor.VisitSlice(custom))...) - - // emits `Object.defineProperty(exports, "__esModule", { value: true });` at the top of the file - if tx.shouldEmitUnderscoreUnderscoreESModule() { - statements = append(statements, tx.createUnderscoreUnderscoreESModule()) - } - - // initialize all exports to `undefined`, e.g.: - // exports.a = exports.b = void 0; - if len(tx.currentModuleInfo.exportedNames) > 0 { - const chunkSize = 50 - l := len(tx.currentModuleInfo.exportedNames) - for i := 0; i < l; i += chunkSize { - right := tx.Factory().NewVoidZeroExpression() - for _, nextId := range tx.currentModuleInfo.exportedNames[i:min(i+chunkSize, l)] { - var left *ast.Expression - if nextId.Kind == ast.KindStringLiteral { - left = tx.Factory().NewElementAccessExpression( - tx.Factory().NewIdentifier("exports"), - nil, /*questionDotToken*/ - tx.Factory().NewStringLiteralFromNode(nextId), - ast.NodeFlagsNone, - ) - } else { - name := nextId.Clone(tx.Factory()) - tx.EmitContext().SetEmitFlags(name, printer.EFNoSourceMap) // TODO: Strada emits comments here, but shouldn't - left = tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("exports"), - nil, /*questionDotToken*/ - name, - ast.NodeFlagsNone, - ) - } - right = tx.Factory().NewAssignmentExpression(left, right) - } - statement := tx.Factory().NewExpressionStatement(right) - tx.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue) - statements = append(statements, statement) - } - } - - // initialize exports for function declarations, e.g.: - // exports.f = f; - // function f() {} - for f := range tx.currentModuleInfo.exportedFunctions.Values() { - statements = tx.appendExportsOfClassOrFunctionDeclaration(statements, f.AsNode()) - } - - // visit the remaining statements in the source file - rest, _ = tx.topLevelVisitor.VisitSlice(rest) - statements = append(statements, rest...) - - // emit `module.exports = ...` if needd - statements = tx.appendExportEqualsIfNeeded(statements) - - // merge temp variables into the statement list - statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements) - - statementList := tx.Factory().NewNodeList(statements) - statementList.Loc = node.Statements.Loc - result := tx.Factory().UpdateSourceFile(node, statementList, node.EndOfFileToken).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 { - prologue, rest := tx.Factory().SplitStandardPrologue(result.Statements.Nodes) - custom, rest := tx.Factory().SplitCustomPrologue(rest) - statements := slices.Clone(prologue) - statements = append(statements, custom...) - statements = append(statements, tx.topLevelVisitor.VisitNode(externalHelpersImportDeclaration)) - statements = append(statements, rest...) - statementList := tx.Factory().NewNodeList(statements) - statementList.Loc = result.Statements.Loc - result = tx.Factory().UpdateSourceFile(result, statementList, node.EndOfFileToken).AsSourceFile() - } - - return result.AsNode() -} - -// Adds the down-level representation of `export=` to the statement list if one exists in the source file. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -func (tx *CommonJSModuleTransformer) appendExportEqualsIfNeeded(statements []*ast.Statement) []*ast.Statement { - if tx.currentModuleInfo.exportEquals != nil { - expressionResult := tx.Visitor().VisitNode(tx.currentModuleInfo.exportEquals.Expression) - if expressionResult != nil { - statement := tx.Factory().NewExpressionStatement( - tx.Factory().NewAssignmentExpression( - tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("module"), - nil, /*questionDotToken*/ - tx.Factory().NewIdentifier("exports"), - ast.NodeFlagsNone, - ), - expressionResult, - ), - ) - - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, tx.currentModuleInfo.exportEquals.AsNode()) - tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments) - statements = append(statements, statement) - } - } - return statements -} - -// Appends the exports of an ImportDeclaration to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `decl` parameter is the declaration whose exports are to be recorded. -func (tx *CommonJSModuleTransformer) appendExportsOfImportDeclaration(statements []*ast.Statement, decl *ast.ImportDeclaration) []*ast.Statement { - if tx.currentModuleInfo.exportEquals != nil { - return statements - } - - importClause := decl.ImportClause - if importClause == nil { - return statements - } - - seen := &collections.Set[string]{} - if importClause.Name() != nil { - statements = tx.appendExportsOfDeclaration(statements, importClause, seen, false /*liveBinding*/) - } - - namedBindings := importClause.AsImportClause().NamedBindings - if namedBindings != nil { - switch namedBindings.Kind { - case ast.KindNamespaceImport: - statements = tx.appendExportsOfDeclaration(statements, namedBindings, seen, false /*liveBinding*/) - - case ast.KindNamedImports: - for _, importBinding := range namedBindings.AsNamedImports().Elements.Nodes { - statements = tx.appendExportsOfDeclaration(statements, importBinding, seen, true /*liveBinding*/) - } - } - } - - return statements -} - -// Appends the exports of a VariableStatement to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `node` parameter is the VariableStatement whose exports are to be recorded. -func (tx *CommonJSModuleTransformer) appendExportsOfVariableStatement(statements []*ast.Statement, node *ast.VariableStatement) []*ast.Statement { - return tx.appendExportsOfVariableDeclarationList(statements, node.DeclarationList.AsVariableDeclarationList() /*isForInOrOfInitializer*/, false) -} - -// Appends the exports of a VariableDeclarationList to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `node` parameter is the VariableDeclarationList whose exports are to be recorded. -func (tx *CommonJSModuleTransformer) appendExportsOfVariableDeclarationList(statements []*ast.Statement, node *ast.VariableDeclarationList, isForInOrOfInitializer bool) []*ast.Statement { - if tx.currentModuleInfo.exportEquals != nil { - return statements - } - - for _, decl := range node.Declarations.Nodes { - statements = tx.appendExportsOfBindingElement(statements, decl, isForInOrOfInitializer) - } - - return statements -} - -// Appends the exports of a VariableDeclaration or BindingElement to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `decl` parameter is the declaration whose exports are to be recorded. -func (tx *CommonJSModuleTransformer) appendExportsOfBindingElement(statements []*ast.Statement, decl *ast.Node /*VariableDeclaration | BindingElement*/, isForInOrOfInitializer bool) []*ast.Statement { - if tx.currentModuleInfo.exportEquals != nil || decl.Name() == nil { - return statements - } - - if ast.IsBindingPattern(decl.Name()) { - for _, element := range decl.Name().AsBindingPattern().Elements.Nodes { - e := element.AsBindingElement() - if e.DotDotDotToken == nil && e.Name() == nil { - statements = tx.appendExportsOfBindingElement(statements, element, isForInOrOfInitializer) - } - } - } else if !transformers.IsGeneratedIdentifier(tx.EmitContext(), decl.Name()) && - (!ast.IsVariableDeclaration(decl) || decl.Initializer() != nil || isForInOrOfInitializer) { - statements = tx.appendExportsOfDeclaration(statements, decl, nil /*seen*/, false /*liveBinding*/) - } - - return statements -} - -// Appends the exports of a ClassDeclaration or FunctionDeclaration to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `decl` parameter is the declaration whose exports are to be recorded. -func (tx *CommonJSModuleTransformer) appendExportsOfClassOrFunctionDeclaration(statements []*ast.Statement, decl *ast.Declaration) []*ast.Statement { - if tx.currentModuleInfo.exportEquals != nil { - return statements - } - - seen := &collections.Set[string]{} - if ast.HasSyntacticModifier(decl, ast.ModifierFlagsExport) { - var exportName *ast.IdentifierNode - if ast.HasSyntacticModifier(decl, ast.ModifierFlagsDefault) { - exportName = tx.Factory().NewIdentifier("default") - } else { - exportName = tx.Factory().GetDeclarationName(decl) - } - - exportValue := tx.Factory().GetLocalName(decl) - statements = tx.appendExportStatement(statements, seen, exportName, exportValue, &decl.Loc, false /*allowComments*/, false /*liveBinding*/) - } - - if decl.Name() != nil { - return tx.appendExportsOfDeclaration(statements, decl, seen, false /*liveBinding*/) - } - - return statements -} - -// Appends the exports of a declaration to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `decl` parameter is the declaration to export. -func (tx *CommonJSModuleTransformer) appendExportsOfDeclaration(statements []*ast.Statement, decl *ast.Declaration, seen *collections.Set[string], liveBinding bool) []*ast.Statement { - if tx.currentModuleInfo.exportEquals != nil { - return statements - } - - if seen == nil { - seen = &collections.Set[string]{} - } - - if name := decl.Name(); tx.currentModuleInfo.exportSpecifiers.Len() > 0 && name != nil && ast.IsIdentifier(name) { - name = tx.Factory().GetDeclarationName(decl) - exportSpecifiers := tx.currentModuleInfo.exportSpecifiers.Get(name.Text()) - if len(exportSpecifiers) > 0 { - exportValue := tx.visitExpressionIdentifier(name) - for _, exportSpecifier := range exportSpecifiers { - statements = tx.appendExportStatement(statements, seen, exportSpecifier.Name(), exportValue, &exportSpecifier.Name().Loc /*location*/, false /*allowComments*/, liveBinding) - } - } - } - - return statements -} - -// Appends the down-level representation of an export to a statement list, returning the statement list. -// -// - The `statements` parameter is a statement list to which the down-level export statements are to be appended. -// - The `exportName` parameter is the name of the export. -// - The `expression` parameter is the expression to export. -// - The `location` parameter is the location to use for source maps and comments for the export. -// - The `allowComments` parameter indicates whether to allow comments on the export. -func (tx *CommonJSModuleTransformer) appendExportStatement(statements []*ast.Statement, seen *collections.Set[string], exportName *ast.ModuleExportName, expression *ast.Expression, location *core.TextRange, allowComments bool, liveBinding bool) []*ast.Statement { - if exportName.Kind != ast.KindStringLiteral { - if seen.Has(exportName.Text()) { - return statements - } - seen.Add(exportName.Text()) - } - statements = append(statements, tx.createExportStatement(exportName, expression, location, allowComments, liveBinding)) - return statements -} - -// Creates a call to the current file's export function to export a value. -// -// - The `name` parameter is the bound name of the export. -// - The `value` parameter is the exported value. -// - The `location` parameter is the location to use for source maps and comments for the export. -// - The `allowComments` parameter indicates whether to emit comments for the statement. -func (tx *CommonJSModuleTransformer) createExportStatement(name *ast.ModuleExportName, value *ast.Expression, location *core.TextRange, allowComments bool, liveBinding bool) *ast.Statement { - statement := tx.Factory().NewExpressionStatement(tx.createExportExpression(name, value, nil /*location*/, liveBinding)) - if location != nil { - tx.EmitContext().SetCommentRange(statement, *location) - } - tx.EmitContext().AddEmitFlags(statement, printer.EFStartOnNewLine) - if !allowComments { - tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments) - } - return statement -} - -// Creates a call to the current file's export function to export a value. -// -// - The `name` parameter is the bound name of the export. -// - The `value` parameter is the exported value. -// - The `location` parameter is the location to use for source maps and comments for the export. -func (tx *CommonJSModuleTransformer) createExportExpression(name *ast.ModuleExportName, value *ast.Expression, location *core.TextRange, liveBinding bool) *ast.Expression { - var expression *ast.Expression - if liveBinding { - // For a live binding we emit a getter on `exports` that returns the value: - // Object.defineProperty(exports, "", { enumerable: true, get: function () { return ; } }); - expression = tx.Factory().NewCallExpression( - tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("Object"), - nil, /*questionDotToken*/ - tx.Factory().NewIdentifier("defineProperty"), - ast.NodeFlagsNone, - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewIdentifier("exports"), - tx.Factory().NewStringLiteralFromNode(name), - tx.Factory().NewObjectLiteralExpression( - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewPropertyAssignment( - nil, /*modifiers*/ - tx.Factory().NewIdentifier("enumerable"), - nil, /*postfixToken*/ - nil, /*typeNode*/ - tx.Factory().NewTrueExpression(), - ), - tx.Factory().NewPropertyAssignment( - nil, /*modifiers*/ - tx.Factory().NewIdentifier("get"), - nil, /*postfixToken*/ - nil, /*typeNode*/ - tx.Factory().NewFunctionExpression( - nil, /*modifiers*/ - nil, /*asteriskToken*/ - nil, /*name*/ - nil, /*typeParameters*/ - tx.Factory().NewNodeList([]*ast.Node{}), - nil, /*type*/ - nil, /*fullSignature*/ - tx.Factory().NewBlock( - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewReturnStatement(value), - }), - false, /*multiLine*/ - ), - ), - ), - }), - false, /*multiLine*/ - ), - }), - ast.NodeFlagsNone, - ) - } else { - // Otherwise, we emit a simple property assignment. - var left *ast.Expression - if name.Kind == ast.KindStringLiteral { - // emits: - // exports[""] = ; - left = tx.Factory().NewElementAccessExpression( - tx.Factory().NewIdentifier("exports"), - nil, /*questionDotToken*/ - tx.Factory().NewStringLiteralFromNode(name), - ast.NodeFlagsNone, - ) - } else { - // emits: - // exports. = ; - left = tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("exports"), - nil, /*questionDotToken*/ - name.Clone(tx.Factory()), - ast.NodeFlagsNone, - ) - } - expression = tx.Factory().NewAssignmentExpression(left, value) - } - if location != nil { - tx.EmitContext().SetCommentRange(expression, *location) - } - return expression -} - -// Creates a `require()` call to import an external module. -func (tx *CommonJSModuleTransformer) createRequireCall(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) *ast.Node { - var args []*ast.Expression - moduleName := getExternalModuleNameLiteral(tx.Factory(), node, tx.currentSourceFile, nil /*host*/, nil /*resolver*/, tx.compilerOptions) - if moduleName != nil { - args = append(args, rewriteModuleSpecifier(tx.EmitContext(), moduleName, tx.compilerOptions)) - } - return tx.Factory().NewCallExpression( - tx.Factory().NewIdentifier("require"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Factory().NewNodeList(args), - ast.NodeFlagsNone) -} - -func (tx *CommonJSModuleTransformer) getHelperExpressionForExport(node *ast.ExportDeclaration, innerExpr *ast.Expression) *ast.Expression { - if !tx.compilerOptions.GetESModuleInterop() || tx.EmitContext().EmitFlags(node.AsNode())&printer.EFNeverApplyImportHelper != 0 { - return innerExpr - } - if getExportNeedsImportStarHelper(node) { - return tx.Visitor().VisitNode(tx.Factory().NewImportStarHelper(innerExpr)) - } - return innerExpr -} - -func (tx *CommonJSModuleTransformer) getHelperExpressionForImport(node *ast.ImportDeclaration, innerExpr *ast.Expression) *ast.Expression { - if !tx.compilerOptions.GetESModuleInterop() || tx.EmitContext().EmitFlags(node.AsNode())&printer.EFNeverApplyImportHelper != 0 { - return innerExpr - } - if getImportNeedsImportStarHelper(node) { - return tx.Visitor().VisitNode(tx.Factory().NewImportStarHelper(innerExpr)) - } - if getImportNeedsImportDefaultHelper(node) { - return tx.Visitor().VisitNode(tx.Factory().NewImportDefaultHelper(innerExpr)) - } - return innerExpr -} - -func (tx *CommonJSModuleTransformer) visitTopLevelImportDeclaration(node *ast.ImportDeclaration) *ast.Node { - if node.ImportClause == nil { - // import "mod"; - statement := tx.Factory().NewExpressionStatement(tx.createRequireCall(node.AsNode())) - tx.EmitContext().SetOriginal(statement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode()) - return statement - } - - var statements []*ast.Statement - var variables []*ast.VariableDeclarationNode - namespaceDeclaration := ast.GetNamespaceDeclarationNode(node.AsNode()) - if namespaceDeclaration != nil && !ast.IsDefaultImport(node.AsNode()) { - // import * as n from "mod"; - variables = append(variables, - tx.Factory().NewVariableDeclaration( - namespaceDeclaration.Name().Clone(tx.Factory()), - nil, /*exclamationToken*/ - nil, /*type*/ - tx.getHelperExpressionForImport(node, tx.createRequireCall(node.AsNode())), - ), - ) - } else { - // import d from "mod"; - // import { x, y } from "mod"; - // import d, { x, y } from "mod"; - // import d, * as n from "mod"; - variables = append(variables, - tx.Factory().NewVariableDeclaration( - tx.Factory().NewGeneratedNameForNode(node.AsNode()), - nil, /*exclamationToken*/ - nil, /*type*/ - tx.getHelperExpressionForImport(node, tx.createRequireCall(node.AsNode())), - ), - ) - - if namespaceDeclaration != nil && ast.IsDefaultImport(node.AsNode()) { - variables = append(variables, - tx.Factory().NewVariableDeclaration( - namespaceDeclaration.Name().Clone(tx.Factory()), - nil, /*exclamationToken*/ - nil, /*type*/ - tx.Factory().NewGeneratedNameForNode(node.AsNode()), - ), - ) - } - } - - varStatement := tx.Factory().NewVariableStatement( - nil, /*modifiers*/ - tx.Factory().NewVariableDeclarationList( - ast.NodeFlagsConst, - tx.Factory().NewNodeList(variables), - ), - ) - - tx.EmitContext().SetOriginal(varStatement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode()) - statements = append(statements, varStatement) - statements = tx.appendExportsOfImportDeclaration(statements, node) - return transformers.SingleOrMany(statements, tx.Factory()) -} - -func (tx *CommonJSModuleTransformer) visitTopLevelImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) *ast.Node { - if !ast.IsExternalModuleImportEqualsDeclaration(node.AsNode()) { - // import m = n; - panic("import= for internal module references should be handled in an earlier transformer.") - } - - var statements []*ast.Statement - if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) { - // export import m = require("mod"); - statement := tx.Factory().NewExpressionStatement( - tx.createExportExpression( - node.Name(), - tx.createRequireCall(node.AsNode()), - &node.Loc, - false, /*liveBinding*/ - ), - ) - - tx.EmitContext().SetOriginal(statement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode()) - statements = append(statements, statement) - } else { - // import m = require("mod"); - statement := tx.Factory().NewVariableStatement( - nil, /*modifiers*/ - tx.Factory().NewVariableDeclarationList( - ast.NodeFlagsConst, - tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{ - tx.Factory().NewVariableDeclaration( - node.Name().Clone(tx.Factory()), - nil, /*exclamationToken*/ - nil, /*typeNode*/ - tx.createRequireCall(node.AsNode()), - ), - }), - ), - ) - tx.EmitContext().SetOriginal(statement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode()) - statements = append(statements, statement) - } - - statements = tx.appendExportsOfDeclaration(statements, node.AsNode(), nil /*seen*/, false /*liveBinding*/) - return transformers.SingleOrMany(statements, tx.Factory()) -} - -func (tx *CommonJSModuleTransformer) visitTopLevelExportDeclaration(node *ast.ExportDeclaration) *ast.Node { - if node.ModuleSpecifier == nil { - // Elide export declarations with no module specifier as they are handled - // elsewhere. - return nil - } - - generatedName := tx.Factory().NewGeneratedNameForNode(node.AsNode()) - if node.ExportClause != nil && ast.IsNamedExports(node.ExportClause) { - // export { x, y } from "mod"; - var statements []*ast.Statement - varStatement := tx.Factory().NewVariableStatement( - nil, /*modifiers*/ - tx.Factory().NewVariableDeclarationList( - ast.NodeFlagsConst, - tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{ - tx.Factory().NewVariableDeclaration( - generatedName, - nil, /*exclamationToken*/ - nil, /*type*/ - tx.createRequireCall(node.AsNode()), - ), - }), - ), - ) - tx.EmitContext().SetOriginal(varStatement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode()) - statements = append(statements, varStatement) - - for _, specifier := range node.ExportClause.AsNamedExports().Elements.Nodes { - specifierName := specifier.PropertyNameOrName() - exportNeedsImportDefault := tx.compilerOptions.GetESModuleInterop() && - tx.EmitContext().EmitFlags(node.AsNode())&printer.EFNeverApplyImportHelper == 0 && - ast.ModuleExportNameIsDefault(specifierName) - - var target *ast.Node - if exportNeedsImportDefault { - target = tx.Factory().NewImportDefaultHelper(generatedName) - } else { - target = generatedName - } - - var exportName *ast.Node - if ast.IsStringLiteral(specifier.Name()) { - exportName = tx.Factory().NewStringLiteralFromNode(specifier.Name()) - } else { - exportName = tx.Factory().GetExportName(specifier.AsNode()) - } - - var exportedValue *ast.Node - if ast.IsStringLiteral(specifierName) { - exportedValue = tx.Factory().NewElementAccessExpression(target, nil /*questionDotToken*/, specifierName, ast.NodeFlagsNone) - } else { - exportedValue = tx.Factory().NewPropertyAccessExpression(target, nil /*questionDotToken*/, specifierName, ast.NodeFlagsNone) - } - statement := tx.Factory().NewExpressionStatement( - tx.createExportExpression( - exportName, - exportedValue, - nil, /*location*/ - true, /*liveBinding*/ - ), - ) - tx.EmitContext().SetOriginal(statement, specifier.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, specifier.AsNode()) - statements = append(statements, statement) - } - - return transformers.SingleOrMany(statements, tx.Factory()) - } - - if node.ExportClause != nil { - // export * as ns from "mod"; - // export * as default from "mod"; - var exportName *ast.Node - if ast.IsStringLiteral(node.ExportClause.Name()) { - exportName = tx.Factory().NewStringLiteralFromNode(node.ExportClause.Name()) - } else { - exportName = node.ExportClause.Name().Clone(tx.Factory()) - } - statement := tx.Factory().NewExpressionStatement( - tx.createExportExpression( - exportName, - tx.getHelperExpressionForExport( - node, - tx.createRequireCall(node.AsNode()), - ), - nil, /*location*/ - false, /*liveBinding*/ - ), - ) - tx.EmitContext().SetOriginal(statement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode()) - return statement - } - - // export * from "mod"; - statement := tx.Factory().NewExpressionStatement( - tx.Visitor().VisitNode(tx.Factory().NewExportStarHelper(tx.createRequireCall(node.AsNode()), tx.Factory().NewIdentifier("exports"))), - ) - tx.EmitContext().SetOriginal(statement, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode()) - return statement -} - -func (tx *CommonJSModuleTransformer) visitTopLevelExportAssignment(node *ast.ExportAssignment) *ast.Node { - if node.IsExportEquals { - return nil - } - - return tx.createExportStatement( - tx.Factory().NewIdentifier("default"), - tx.Visitor().VisitNode(node.Expression), - &node.Loc, /*location*/ - true, /*allowComments*/ - false, /*liveBinding*/ - ) -} - -func (tx *CommonJSModuleTransformer) visitTopLevelFunctionDeclaration(node *ast.FunctionDeclaration) *ast.Node { - if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) { - return tx.Factory().UpdateFunctionDeclaration( - node, - transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault), - node.AsteriskToken, - tx.Factory().GetDeclarationName(node.AsNode()), - nil, /*typeParameters*/ - tx.Visitor().VisitNodes(node.Parameters), - nil, /*type*/ - nil, /*fullSignature*/ - tx.Visitor().VisitNode(node.Body), - ) - } else { - return tx.Visitor().VisitEachChild(node.AsNode()) - } -} - -func (tx *CommonJSModuleTransformer) visitTopLevelClassDeclaration(node *ast.ClassDeclaration) *ast.Node { - var statements []*ast.Statement - if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) { - statements = append(statements, tx.Factory().UpdateClassDeclaration( - node, - tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault)), - tx.Factory().GetDeclarationName(node.AsNode()), - nil, /*typeParameters*/ - tx.Visitor().VisitNodes(node.HeritageClauses), - tx.Visitor().VisitNodes(node.Members), - )) - } else { - statements = append(statements, tx.Visitor().VisitEachChild(node.AsNode())) - } - statements = tx.appendExportsOfClassOrFunctionDeclaration(statements, node.AsNode()) - return transformers.SingleOrMany(statements, tx.Factory()) -} - -func (tx *CommonJSModuleTransformer) visitTopLevelVariableStatement(node *ast.VariableStatement) *ast.Node { - var statements []*ast.Statement - if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) { - // export var a = b; - var variables []*ast.VariableDeclarationNode - var expressions []*ast.Expression - var modifiers *ast.ModifierList - - commitPendingVariables := func() { - if len(variables) > 0 { - variableList := tx.Factory().NewNodeList(variables) - variableList.Loc = node.DeclarationList.AsVariableDeclarationList().Declarations.Loc - statement := tx.Factory().UpdateVariableStatement( - node, - modifiers, - tx.Factory().UpdateVariableDeclarationList( - node.DeclarationList.AsVariableDeclarationList(), - variableList, - ), - ) - if len(statements) > 0 { - tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments) - } - statements = append(statements, statement) - variables = nil - } - } - - commitPendingExpressions := func() { - if len(expressions) > 0 { - statement := tx.Factory().NewExpressionStatement(tx.Factory().InlineExpressions(expressions)) - tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode()) - if len(statements) > 0 { - tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments) - } - statements = append(statements, statement) - expressions = nil - } - } - - pushVariable := func(variable *ast.VariableDeclarationNode) { - commitPendingExpressions() - variables = append(variables, variable) - } - - pushExpression := func(expression *ast.Expression) { - commitPendingVariables() - expressions = append(expressions, expression) - } - - // If we're exporting these variables, then these just become assignments to 'exports.x'. - for _, variable := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes { - v := variable.AsVariableDeclaration() - - if ast.IsIdentifier(v.Name()) && transformers.IsLocalName(tx.EmitContext(), v.Name()) { - // A "local name" generally means a variable declaration that *shouldn't* be - // converted to `exports.x = ...`, even if the declaration is exported. This - // usually indicates a class or function declaration that was converted into - // a variable declaration, as most references to the declaration will remain - // untransformed (i.e., `new C` rather than `new exports.C`). In these cases, - // an `export { x }` declaration will follow. - - if modifiers == nil { - modifiers = transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault) - } - - if v.Initializer != nil { - variable = tx.Factory().UpdateVariableDeclaration( - v, - v.Name(), - nil, /*exclamationToken*/ - nil, /*type*/ - tx.createExportExpression( - v.Name(), - tx.Visitor().VisitNode(v.Initializer), - nil, - false, /*liveBinding*/ - ), - ) - } - - pushVariable(variable) - } else if v.Initializer != nil && !ast.IsBindingPattern(v.Name()) && (ast.IsArrowFunction(v.Initializer) || (ast.IsFunctionExpression(v.Initializer) || ast.IsClassExpression(v.Initializer)) && v.Initializer.Name() == nil) { - // preserve variable declarations for functions and classes to assign names - - pushVariable(tx.Factory().NewVariableDeclaration( - v.Name(), - v.ExclamationToken, - v.Type, - tx.Visitor().VisitNode(v.Initializer), - )) - - propertyAccess := tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("exports"), - nil, /*questionDotToken*/ - v.Name(), - ast.NodeFlagsNone, - ) - tx.EmitContext().AssignCommentAndSourceMapRanges(propertyAccess, v.Name()) - - pushExpression(tx.Factory().NewAssignmentExpression( - propertyAccess, - v.Name().Clone(tx.Factory()), - )) - } else { - expression := transformers.ConvertVariableDeclarationToAssignmentExpression(tx.EmitContext(), v) - if expression != nil { - pushExpression(tx.Visitor().VisitNode(expression)) - } - } - } - - commitPendingVariables() - commitPendingExpressions() - statements = tx.appendExportsOfVariableStatement(statements, node) - return transformers.SingleOrMany(statements, tx.Factory()) - } - return tx.visitTopLevelNestedVariableStatement(node) -} - -// Visits a top-level nested variable statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedVariableStatement(node *ast.VariableStatement) *ast.Node { - var statements []*ast.Statement - statements = append(statements, tx.Visitor().VisitEachChild(node.AsNode())) - statements = tx.appendExportsOfVariableStatement(statements, node) - return transformers.SingleOrMany(statements, tx.Factory()) -} - -// Visits a top-level nested `for` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedForStatement(node *ast.ForStatement) *ast.Node { - if node.Initializer != nil && ast.IsVariableDeclarationList(node.Initializer) && node.Initializer.Flags&ast.NodeFlagsBlockScoped == 0 { - exportStatements := tx.appendExportsOfVariableDeclarationList(nil /*statements*/, node.Initializer.AsVariableDeclarationList(), false /*isForInOrOfInitializer*/) - if len(exportStatements) > 0 { - // given: - // export { x } - // for (var x = 0; ;) { } - // emits: - // var x = 0; - // exports.x = x; - // for (; ;) { } - - var statements []*ast.Statement - varDeclList := tx.discardedValueVisitor.VisitNode(node.Initializer) - varStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, varDeclList) - statements = append(statements, varStatement) - statements = append(statements, exportStatements...) - - condition := tx.Visitor().VisitNode(node.Condition) - incrementor := tx.discardedValueVisitor.VisitNode(node.Incrementor) - body := tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor) - statements = append(statements, tx.Factory().UpdateForStatement( - node, - nil, /*initializer*/ - condition, - incrementor, - body, - )) - return transformers.SingleOrMany(statements, tx.Factory()) - } - } - return tx.Factory().UpdateForStatement( - node, - tx.discardedValueVisitor.VisitNode(node.Initializer), - tx.Visitor().VisitNode(node.Condition), - tx.discardedValueVisitor.VisitNode(node.Incrementor), - tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor), - ) -} - -// Visits a top-level nested `for..in` or `for..of` statement as it may contain `var` declarations that are hoisted and -// may still be exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedForInOrOfStatement(node *ast.ForInOrOfStatement) *ast.Node { - if ast.IsVariableDeclarationList(node.Initializer) && node.Initializer.Flags&ast.NodeFlagsBlockScoped == 0 { - exportStatements := tx.appendExportsOfVariableDeclarationList(nil /*statements*/, node.Initializer.AsVariableDeclarationList(), true /*isForInOrOfInitializer*/) - if len(exportStatements) > 0 { - // given: - // export { x } - // for (var x in y) { - // ... - // } - // emits: - // for (var x in y) { - // exports.x = x; - // ... - // } - - initializer := tx.discardedValueVisitor.VisitNode(node.Initializer) - expression := tx.Visitor().VisitNode(node.Expression) - body := tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor) - if ast.IsBlock(body) { - block := body.AsBlock() - bodyStatements := append(exportStatements, block.Statements.Nodes...) - bodyStatementList := tx.Factory().NewNodeList(bodyStatements) - bodyStatementList.Loc = block.Statements.Loc - body = tx.Factory().UpdateBlock(block, bodyStatementList) - } else { - bodyStatements := append(exportStatements, body) - body = tx.Factory().NewBlock(tx.Factory().NewNodeList(bodyStatements), true /*multiLine*/) - } - return tx.Factory().UpdateForInOrOfStatement(node, node.AwaitModifier, initializer, expression, body) - } - } - return tx.Factory().UpdateForInOrOfStatement( - node, - node.AwaitModifier, - tx.discardedValueVisitor.VisitNode(node.Initializer), - tx.Visitor().VisitNode(node.Expression), - tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor), - ) -} - -// Visits a top-level nested `do` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedDoStatement(node *ast.DoStatement) *ast.Node { - return tx.Factory().UpdateDoStatement( - node, - tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor), - tx.Visitor().VisitNode(node.Expression), - ) -} - -// Visits a top-level nested `while` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedWhileStatement(node *ast.WhileStatement) *ast.Node { - return tx.Factory().UpdateWhileStatement( - node, - tx.Visitor().VisitNode(node.Expression), - tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor), - ) -} - -// Visits a top-level nested labeled statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedLabeledStatement(node *ast.LabeledStatement) *ast.Node { - return tx.Factory().UpdateLabeledStatement( - node, - node.Label, - tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.Statement), - ) -} - -// Visits a top-level nested `with` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedWithStatement(node *ast.WithStatement) *ast.Node { - return tx.Factory().UpdateWithStatement( - node, - tx.Visitor().VisitNode(node.Expression), - tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.Statement), - ) -} - -// Visits a top-level nested `if` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedIfStatement(node *ast.IfStatement) *ast.Node { - return tx.Factory().UpdateIfStatement( - node, - tx.Visitor().VisitNode(node.Expression), - tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.ThenStatement), - tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.ElseStatement), - ) -} - -// Visits a top-level nested `switch` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedSwitchStatement(node *ast.SwitchStatement) *ast.Node { - return tx.Factory().UpdateSwitchStatement( - node, - tx.Visitor().VisitNode(node.Expression), - tx.topLevelNestedVisitor.VisitNode(node.CaseBlock), - ) -} - -// Visits a top-level nested case block as it may contain `var` declarations that are hoisted and may still be exported -// with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedCaseBlock(node *ast.CaseBlock) *ast.Node { - return tx.topLevelNestedVisitor.VisitEachChild(node.AsNode()) -} - -// Visits a top-level nested `case` or `default` clause as it may contain `var` declarations that are hoisted and may -// still be exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedCaseOrDefaultClause(node *ast.CaseOrDefaultClause) *ast.Node { - return tx.Factory().UpdateCaseOrDefaultClause( - node, - tx.Visitor().VisitNode(node.Expression), - tx.topLevelNestedVisitor.VisitNodes(node.Statements), - ) -} - -// Visits a top-level nested `try` statement as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedTryStatement(node *ast.TryStatement) *ast.Node { - return tx.topLevelNestedVisitor.VisitEachChild(node.AsNode()) -} - -// Visits a top-level nested `catch` clause as it may contain `var` declarations that are hoisted and may still be -// exported with `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedCatchClause(node *ast.CatchClause) *ast.Node { - return tx.Factory().UpdateCatchClause( - node, - node.VariableDeclaration, - tx.topLevelNestedVisitor.VisitNode(node.Block), - ) -} - -// Visits a top-level nested block as it may contain `var` declarations that are hoisted and may still be exported with -// `export {}`. -func (tx *CommonJSModuleTransformer) visitTopLevelNestedBlock(node *ast.Block) *ast.Node { - return tx.topLevelNestedVisitor.VisitEachChild(node.AsNode()) -} - -func (tx *CommonJSModuleTransformer) visitForStatement(node *ast.ForStatement) *ast.Node { - return tx.Factory().UpdateForStatement( - node, - tx.discardedValueVisitor.VisitNode(node.Initializer), - tx.Visitor().VisitNode(node.Condition), - tx.discardedValueVisitor.VisitNode(node.Incrementor), - tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor), - ) -} - -func (tx *CommonJSModuleTransformer) visitForInOrOfStatement(node *ast.ForInOrOfStatement) *ast.Node { - return tx.Factory().UpdateForInOrOfStatement( - node, - node.AwaitModifier, - tx.discardedValueVisitor.VisitNode(node.Initializer), - tx.Visitor().VisitNode(node.Expression), - tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor), - ) -} - -// Visits an expression statement whose value will be discarded at runtime. -func (tx *CommonJSModuleTransformer) visitExpressionStatement(node *ast.ExpressionStatement) *ast.Node { - return tx.discardedValueVisitor.VisitEachChild(node.AsNode()) -} - -// Visits a `void` expression whose value will be discarded at runtime. -func (tx *CommonJSModuleTransformer) visitVoidExpression(node *ast.VoidExpression) *ast.Node { - return tx.discardedValueVisitor.VisitEachChild(node.AsNode()) -} - -// Visits a parenthesized expression whose value may be discarded at runtime. -func (tx *CommonJSModuleTransformer) visitParenthesizedExpression(node *ast.ParenthesizedExpression, resultIsDiscarded bool) *ast.Node { - expression := core.IfElse(resultIsDiscarded, tx.discardedValueVisitor, tx.Visitor()).VisitNode(node.Expression) - return tx.Factory().UpdateParenthesizedExpression(node, expression) -} - -// Visits a partially emitted expression whose value may be discarded at runtime. -func (tx *CommonJSModuleTransformer) visitPartiallyEmittedExpression(node *ast.PartiallyEmittedExpression, resultIsDiscarded bool) *ast.Node { - expression := core.IfElse(resultIsDiscarded, tx.discardedValueVisitor, tx.Visitor()).VisitNode(node.Expression) - return tx.Factory().UpdatePartiallyEmittedExpression(node, expression) -} - -// Visits a binary expression whose value may be discarded, or which might contain an assignment to an exported -// identifier. -func (tx *CommonJSModuleTransformer) visitBinaryExpression(node *ast.BinaryExpression, resultIsDiscarded bool) *ast.Node { - if ast.IsAssignmentExpression(node.AsNode(), false /*excludeCompoundAssignment*/) { - return tx.visitAssignmentExpression(node) - } - - if ast.IsCommaExpression(node.AsNode()) { - return tx.visitCommaExpression(node, resultIsDiscarded) - } - - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -func (tx *CommonJSModuleTransformer) visitAssignmentExpression(node *ast.BinaryExpression) *ast.Node { - if ast.IsDestructuringAssignment(node.AsNode()) { - return tx.visitDestructuringAssignment(node) - } - - // When we see an assignment expression whose left-hand side is an exported symbol, - // we should ensure all exports of that symbol are updated with the correct value. - // - // - We do not transform generated identifiers unless they are file-level reserved names. - // - We do not transform identifiers tagged with the LocalName flag. - // - We only transform identifiers that are exported at the top level. - if ast.IsIdentifier(node.Left) && - (!transformers.IsGeneratedIdentifier(tx.EmitContext(), node.Left) || isFileLevelReservedGeneratedIdentifier(tx.EmitContext(), node.Left)) && - !transformers.IsLocalName(tx.EmitContext(), node.Left) { - exportedNames := tx.getExports(node.Left) - if len(exportedNames) > 0 { - // For each additional export of the declaration, apply an export assignment. - expression := tx.Visitor().VisitEachChild(node.AsNode()) - for _, exportName := range exportedNames { - expression = tx.createExportExpression(exportName, expression, &node.Loc /*location*/, false /*liveBinding*/) - } - return expression - } - } - - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -// Visits a destructuring assignment which might target an exported identifier. -func (tx *CommonJSModuleTransformer) visitDestructuringAssignment(node *ast.BinaryExpression) *ast.Node { - return tx.Factory().UpdateBinaryExpression( - node, - nil, /*modifiers*/ - tx.assignmentPatternVisitor.VisitNode(node.Left), - nil, /*typeNode*/ - node.OperatorToken, - tx.Visitor().VisitNode(node.Right), - ) -} - -func (tx *CommonJSModuleTransformer) visitAssignmentProperty(node *ast.PropertyAssignment) *ast.Node { - return tx.Factory().UpdatePropertyAssignment( - node, - nil, /*modifiers*/ - tx.Visitor().VisitNode(node.Name()), - nil, /*postfixToken*/ - nil, /*typeNode*/ - tx.assignmentPatternVisitor.VisitNode(node.Initializer), - ) -} - -func (tx *CommonJSModuleTransformer) visitShorthandAssignmentProperty(node *ast.ShorthandPropertyAssignment) *ast.Node { - target := tx.visitDestructuringAssignmentTargetNoStack(node.Name()) - if ast.IsIdentifier(target) { - return tx.Factory().UpdateShorthandPropertyAssignment( - node, - nil, /*modifiers*/ - target, - nil, /*postfixToken*/ - nil, /*typeNode*/ - node.EqualsToken, - tx.Visitor().VisitNode(node.ObjectAssignmentInitializer), - ) - } - if node.ObjectAssignmentInitializer != nil { - equalsToken := node.EqualsToken - if equalsToken == nil { - equalsToken = tx.Factory().NewToken(ast.KindEqualsToken) - } - target = tx.Factory().NewBinaryExpression( - nil, /*modifiers*/ - target, - nil, /*typeNode*/ - equalsToken, - tx.Visitor().VisitNode(node.ObjectAssignmentInitializer), - ) - } - updated := tx.Factory().NewPropertyAssignment( - nil, /*modifiers*/ - node.Name(), - nil, /*postfixToken*/ - nil, /*typeNode*/ - target, - ) - tx.EmitContext().SetOriginal(updated, node.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(updated, node.AsNode()) - return updated -} - -func (tx *CommonJSModuleTransformer) visitAssignmentRestProperty(node *ast.SpreadAssignment) *ast.Node { - return tx.Factory().UpdateSpreadAssignment( - node, - tx.visitDestructuringAssignmentTarget(node.Expression), - ) -} - -func (tx *CommonJSModuleTransformer) visitAssignmentRestElement(node *ast.SpreadElement) *ast.Node { - return tx.Factory().UpdateSpreadElement( - node, - tx.visitDestructuringAssignmentTarget(node.Expression), - ) -} - -func (tx *CommonJSModuleTransformer) visitAssignmentElement(node *ast.Node) *ast.Node { - if ast.IsBinaryExpression(node) { - n := node.AsBinaryExpression() - if n.OperatorToken.Kind == ast.KindEqualsToken { - return tx.Factory().UpdateBinaryExpression( - n, - nil, /*modifiers*/ - tx.visitDestructuringAssignmentTarget(n.Left), - nil, /*typeNode*/ - n.OperatorToken, - tx.Visitor().VisitNode(n.Right), - ) - } - } - - return tx.visitDestructuringAssignmentTargetNoStack(node) -} - -func (tx *CommonJSModuleTransformer) visitDestructuringAssignmentTarget(node *ast.Node) *ast.Node { - grandparentNode := tx.pushNode(node) - defer tx.popNode(grandparentNode) - - switch node.Kind { - case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression: - node = tx.visitAssignmentPatternNoStack(node) - default: - node = tx.visitDestructuringAssignmentTargetNoStack(node) - } - return node -} - -func (tx *CommonJSModuleTransformer) visitDestructuringAssignmentTargetNoStack(node *ast.Node) *ast.Node { - if ast.IsIdentifier(node) && - (!transformers.IsGeneratedIdentifier(tx.EmitContext(), node) || isFileLevelReservedGeneratedIdentifier(tx.EmitContext(), node)) && - !transformers.IsLocalName(tx.EmitContext(), node) { - expression := tx.visitExpressionIdentifier(node) - exportedNames := tx.getExports(node) - if len(exportedNames) > 0 { - // transforms: - // var x; - // export { x } - // { x: x } = y - // to: - // { x: { set value(v) { exports.x = x = v; } }.value } = y - - value := tx.Factory().NewUniqueNameEx("value", printer.AutoGenerateOptions{ - Flags: printer.GeneratedIdentifierFlagsOptimistic, - }) - expression = tx.Factory().NewAssignmentExpression(expression, value) - - for _, exportName := range exportedNames { - expression = tx.createExportExpression(exportName, expression, nil /*location*/, false /*liveBinding*/) - } - - statement := tx.Factory().NewExpressionStatement(expression) - statementList := tx.Factory().NewNodeList([]*ast.Node{statement}) - param := tx.Factory().NewParameterDeclaration( - nil, /*modifiers*/ - nil, /*dotDotDotToken*/ - value, - nil, /*questionToken*/ - nil, /*type*/ - nil, /*initializer*/ - ) - valueSetter := tx.Factory().NewSetAccessorDeclaration( - nil, /*modifiers*/ - tx.Factory().NewIdentifier("value"), - nil, /*typeParameters*/ - tx.Factory().NewNodeList([]*ast.Node{param}), - nil, /*returnType*/ - nil, /*fullSignature*/ - tx.Factory().NewBlock(statementList, false /*multiLine*/), - ) - propertyList := tx.Factory().NewNodeList([]*ast.Node{valueSetter}) - expression = tx.Factory().NewObjectLiteralExpression(propertyList, false /*multiLine*/) - expression = tx.Factory().NewPropertyAccessExpression(expression, nil /*questionDotToken*/, tx.Factory().NewIdentifier("value"), ast.NodeFlagsNone) - } - return expression - } - - return tx.visitNoStack(node, false /*resultIsDiscarded*/) -} - -// Visits a comma expression whose left-hand value is always discard, and whose right-hand value may be discarded at runtime. -func (tx *CommonJSModuleTransformer) visitCommaExpression(node *ast.BinaryExpression, resultIsDiscarded bool) *ast.Node { - left := tx.discardedValueVisitor.VisitNode(node.Left) - right := core.IfElse(resultIsDiscarded, tx.discardedValueVisitor, tx.Visitor()).VisitNode(node.Right) - return tx.Factory().UpdateBinaryExpression(node, nil /*modifiers*/, left, nil /*typeNode*/, node.OperatorToken, right) -} - -// Visits a prefix unary expression that might modify an exported identifier. -func (tx *CommonJSModuleTransformer) visitPrefixUnaryExpression(node *ast.PrefixUnaryExpression, resultIsDiscarded bool) *ast.Node { - // When we see a prefix increment expression whose operand is an exported - // symbol, we should ensure all exports of that symbol are updated with the correct - // value. - // - // - We do not transform generated identifiers for any reason. - // - We do not transform identifiers tagged with the LocalName flag. - // - We do not transform identifiers that were originally the name of an enum or - // namespace due to how they are transformed in TypeScript. - // - We only transform identifiers that are exported at the top level. - if (node.Operator == ast.KindPlusPlusToken || node.Operator == ast.KindMinusMinusToken) && - ast.IsIdentifier(node.Operand) && - !transformers.IsLocalName(tx.EmitContext(), node.Operand) { - exportedNames := tx.getExports(node.Operand) - if len(exportedNames) > 0 { - // given: - // var x = 0; - // export { x } - // ++x; - // emits: - // var x = 0; - // exports.x = x; - // exports.x = ++x; - // note: - // after the operation, `exports.x` will hold the value of `x` after the increment. - - expression := tx.Factory().UpdatePrefixUnaryExpression(node, tx.Visitor().VisitNode(node.Operand)) - for _, exportName := range exportedNames { - expression = tx.createExportExpression(exportName, expression, nil /*location*/, false /*liveBinding*/) - tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode()) - } - return expression - } - } - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -// Visits a postfix unary expression that might modify an exported identifier. -func (tx *CommonJSModuleTransformer) visitPostfixUnaryExpression(node *ast.PostfixUnaryExpression, resultIsDiscarded bool) *ast.Node { - // When we see a postfix increment expression whose operand is an exported - // symbol, we should ensure all exports of that symbol are updated with the correct - // value. - // - // - We do not transform generated identifiers for any reason. - // - We do not transform identifiers tagged with the LocalName flag. - // - We do not transform identifiers that were originally the name of an enum or - // namespace due to how they are transformed in TypeScript. - // - We only transform identifiers that are exported at the top level. - if (node.Operator == ast.KindPlusPlusToken || node.Operator == ast.KindMinusMinusToken) && - ast.IsIdentifier(node.Operand) && - !transformers.IsLocalName(tx.EmitContext(), node.Operand) { - exportedNames := tx.getExports(node.Operand) - if len(exportedNames) > 0 { - // given (value is discarded): - // var x = 0; - // export { x } - // x++; - // emits: - // var x = 0, y; - // exports.x = x; - // exports.x = (x++, x); - // note: - // after the operation, `exports.x` will hold the value of `x` after the increment. - // - // given (value is not discarded): - // var x = 0, y; - // export { x } - // y = x++; - // emits: - // var _a; - // var x = 0, y; - // exports.x = x; - // y = (exports.x = (_a = x++, x), _a); - // note: - // after the operation, `exports.x` will hold the value of `x` after the increment, while - // `y` will hold the value of `x` before the increment. - - var temp *ast.IdentifierNode - expression := tx.Factory().UpdatePostfixUnaryExpression(node, tx.Visitor().VisitNode(node.Operand)) - if !resultIsDiscarded { - temp = tx.Factory().NewTempVariable() - tx.EmitContext().AddVariableDeclaration(temp) - - expression = tx.Factory().NewAssignmentExpression(temp, expression) - tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode()) - } - - expression = tx.Factory().NewCommaExpression(expression, node.Operand.Clone(tx.Factory())) - tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode()) - - for _, exportName := range exportedNames { - expression = tx.createExportExpression(exportName, expression, nil /*location*/, false /*liveBinding*/) - tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode()) - } - - if temp != nil { - expression = tx.Factory().NewCommaExpression(expression, temp.AsNode()) - tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode()) - } - - return expression - } - } - - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -// Visits a call expression that might reference an imported symbol and thus require an indirect call, or that might -// be an `import()` or `require()` call that may need to be rewritten. -func (tx *CommonJSModuleTransformer) visitCallExpression(node *ast.CallExpression) *ast.Node { - needsRewrite := false - 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*/) { - needsRewrite = true - } - } - if ast.IsImportCall(node.AsNode()) && tx.shouldTransformImportCall() { - return tx.visitImportCallExpression(node, needsRewrite) - } - if needsRewrite { - return tx.shimOrRewriteImportOrRequireCall(node.AsCallExpression()) - } - if ast.IsIdentifier(node.Expression) && - !transformers.IsGeneratedIdentifier(tx.EmitContext(), node.Expression) && - !transformers.IsHelperName(tx.EmitContext(), node.Expression) { - // given: - // import { f } from "mod"; - // f(); - // emits: - // const mod_1 = require("mod"); - // (0, mod_1.f)(); - // note: - // the indirect call is applied by the printer by way of the `EFIndirectCall` emit flag. - expression := tx.visitExpressionIdentifier(node.Expression) - updated := tx.Factory().UpdateCallExpression( - node, - expression, - node.QuestionDotToken, - nil, /*typeArguments*/ - tx.Visitor().VisitNodes(node.Arguments), - ) - if !ast.IsIdentifier(expression) { - tx.EmitContext().AddEmitFlags(updated, printer.EFIndirectCall) - } - return updated - } - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -func (tx *CommonJSModuleTransformer) shouldTransformImportCall() bool { - return ast.ShouldTransformImportCall(tx.currentSourceFile.FileName(), tx.compilerOptions, tx.getEmitModuleFormatOfFile(tx.currentSourceFile)) -} - -func (tx *CommonJSModuleTransformer) visitImportCallExpression(node *ast.CallExpression, rewriteOrShim bool) *ast.Node { - if tx.moduleKind == core.ModuleKindNone && tx.languageVersion >= core.ScriptTargetES2020 { - return tx.Visitor().VisitEachChild(node.AsNode()) - } - - externalModuleName := getExternalModuleNameLiteral(tx.Factory(), node.AsNode(), tx.currentSourceFile, nil /*host*/, nil /*resolver*/, tx.compilerOptions) - firstArgument := tx.Visitor().VisitNode(core.FirstOrNil(node.Arguments.Nodes)) - - // Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output. - var argument *ast.Expression - if externalModuleName != nil && (firstArgument == nil || !ast.IsStringLiteral(firstArgument) || firstArgument.Text() != externalModuleName.Text()) { - argument = externalModuleName - } else if firstArgument != nil && rewriteOrShim { - if ast.IsStringLiteral(firstArgument) { - argument = rewriteModuleSpecifier(tx.EmitContext(), firstArgument, tx.compilerOptions) - } else { - argument = tx.Factory().NewRewriteRelativeImportExtensionsHelper(firstArgument, tx.compilerOptions.Jsx == core.JsxEmitPreserve) - } - } else { - argument = firstArgument - } - return tx.createImportCallExpressionCommonJS(argument) -} - -func (tx *CommonJSModuleTransformer) createImportCallExpressionCommonJS(arg *ast.Expression) *ast.Expression { - // import(x) - // emit as - // Promise.resolve(`${x}`).then((s) => require(s)) /*CommonJS Require*/ - // We have to wrap require in then callback so that require is done in asynchronously - // if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately - // If the arg is not inlineable, we have to evaluate and ToString() it in the current scope - // Otherwise, we inline it in require() so that it's statically analyzable - - needSyncEval := arg != nil && !isSimpleInlineableExpression(arg) - - var promiseResolveArguments []*ast.Expression - if needSyncEval { - promiseResolveArguments = []*ast.Expression{ - tx.Factory().NewTemplateExpression( - tx.Factory().NewTemplateHead("", "", ast.TokenFlagsNone), - tx.Factory().NewNodeList([]*ast.TemplateSpanNode{ - tx.Factory().NewTemplateSpan(arg, tx.Factory().NewTemplateTail("", "", ast.TokenFlagsNone)), - }), - ), - } - } - promiseResolveCall := tx.Factory().NewCallExpression( - tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("Promise"), - nil, /*questionDotToken*/ - tx.Factory().NewIdentifier("resolve"), - ast.NodeFlagsNone, - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Factory().NewNodeList(promiseResolveArguments), - ast.NodeFlagsNone, - ) - - var requireArguments []*ast.Expression - if needSyncEval { - requireArguments = []*ast.Expression{ - tx.Factory().NewIdentifier("s"), - } - } else if arg != nil { - requireArguments = []*ast.Expression{arg} - } - - requireCall := tx.Factory().NewCallExpression( - tx.Factory().NewIdentifier("require"), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Factory().NewNodeList(requireArguments), - ast.NodeFlagsNone, - ) - - if tx.compilerOptions.GetESModuleInterop() { - requireCall = tx.Factory().NewImportStarHelper(requireCall) - } - - var parameters []*ast.ParameterDeclarationNode - if needSyncEval { - parameters = []*ast.ParameterDeclarationNode{ - tx.Factory().NewParameterDeclaration( - nil, /*modifiers*/ - nil, /*dotDotDotToken*/ - tx.Factory().NewIdentifier("s"), - nil, /*questionToken*/ - nil, /*type*/ - nil, /*initializer*/ - ), - } - } - - function := tx.Factory().NewArrowFunction( - nil, /*modifiers*/ - nil, /*typeParameters*/ - tx.Factory().NewNodeList(parameters), - nil, /*type*/ - nil, /*fullSignature*/ - tx.Factory().NewToken(ast.KindEqualsGreaterThanToken), /*equalsGreaterThanToken*/ - requireCall, - ) - - downleveledImport := tx.Factory().NewCallExpression( - tx.Factory().NewPropertyAccessExpression( - promiseResolveCall, - nil, /*questionDotToken*/ - tx.Factory().NewIdentifier("then"), - ast.NodeFlagsNone, - ), - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Factory().NewNodeList([]*ast.Expression{function}), - ast.NodeFlagsNone, - ) - return downleveledImport -} - -func (tx *CommonJSModuleTransformer) shimOrRewriteImportOrRequireCall(node *ast.CallExpression) *ast.Node { - expression := tx.Visitor().VisitNode(node.Expression) - argumentsList := node.Arguments - if len(node.Arguments.Nodes) > 0 { - firstArgument := node.Arguments.Nodes[0] - firstArgumentChanged := false - if ast.IsStringLiteralLike(firstArgument) { - rewritten := rewriteModuleSpecifier(tx.EmitContext(), firstArgument, tx.compilerOptions) - firstArgumentChanged = rewritten != firstArgument - firstArgument = rewritten - } else { - firstArgument = tx.Factory().NewRewriteRelativeImportExtensionsHelper(firstArgument, tx.compilerOptions.Jsx == core.JsxEmitPreserve) - firstArgumentChanged = true - } - - rest, restChanged := tx.Visitor().VisitSlice(node.Arguments.Nodes[1:]) - if firstArgumentChanged || restChanged { - arguments := append([]*ast.Expression{firstArgument}, rest...) - argumentsList = tx.Factory().NewNodeList(arguments) - argumentsList.Loc = node.Arguments.Loc - } - } - - return tx.Factory().UpdateCallExpression( - node, - expression, - node.QuestionDotToken, - nil, /*typeArguments*/ - argumentsList, - ) -} - -// Visits a tagged template expression that might reference an imported symbol and thus require an indirect call. -func (tx *CommonJSModuleTransformer) visitTaggedTemplateExpression(node *ast.TaggedTemplateExpression) *ast.Node { - if ast.IsIdentifier(node.Tag) && !transformers.IsGeneratedIdentifier(tx.EmitContext(), node.Tag) && !transformers.IsHelperName(tx.EmitContext(), node.Tag) { - // given: - // import { f } from "mod"; - // f``; - // emits: - // const mod_1 = require("mod"); - // (0, mod_1.f) ``; - // note: - // the indirect call is applied by the printer by way of the `EFIndirectCall` emit flag. - - expression := tx.visitExpressionIdentifier(node.Tag) - updated := tx.Factory().UpdateTaggedTemplateExpression( - node, - expression, - nil, /*questionDotToken*/ - nil, /*typeArguments*/ - tx.Visitor().VisitNode(node.Template), - ) - if !ast.IsIdentifier(expression) { - tx.EmitContext().AddEmitFlags(updated, printer.EFIndirectCall) - } - return updated - } - return tx.Visitor().VisitEachChild(node.AsNode()) -} - -// Visits a shorthand property assignment that might reference an imported or exported symbol. -func (tx *CommonJSModuleTransformer) visitShorthandPropertyAssignment(node *ast.ShorthandPropertyAssignment) *ast.Node { - name := node.Name() - exportedOrImportedName := tx.visitExpressionIdentifier(name) - if exportedOrImportedName != name { - // A shorthand property with an assignment initializer is probably part of a - // destructuring assignment - expression := exportedOrImportedName - if node.ObjectAssignmentInitializer != nil { - expression = tx.Factory().NewAssignmentExpression( - expression, - tx.Visitor().VisitNode(node.ObjectAssignmentInitializer), - ) - } - assignment := tx.Factory().NewPropertyAssignment(nil /*modifiers*/, name, nil /*postfixToken*/, nil /*typeNode*/, expression) - assignment.Loc = node.Loc - tx.EmitContext().AssignCommentAndSourceMapRanges(assignment, node.AsNode()) - return assignment - } - return tx.Factory().UpdateShorthandPropertyAssignment(node, - nil, /*modifiers*/ - exportedOrImportedName, - nil, /*postfixToken*/ - nil, /*typeNode*/ - node.EqualsToken, - tx.Visitor().VisitNode(node.ObjectAssignmentInitializer), - ) -} - -// Visits an identifier that, if it is in an expression position, might reference an imported or exported symbol. -func (tx *CommonJSModuleTransformer) visitIdentifier(node *ast.IdentifierNode) *ast.Node { - if transformers.IsIdentifierReference(node, tx.parentNode) { - return tx.visitExpressionIdentifier(node) - } - return node -} - -// Visits an identifier in an expression position that might reference an imported or exported symbol. -func (tx *CommonJSModuleTransformer) visitExpressionIdentifier(node *ast.IdentifierNode) *ast.Node { - if info := tx.EmitContext().GetAutoGenerateInfo(node); !(info != nil && !info.Flags.HasAllowNameSubstitution()) && - !transformers.IsHelperName(tx.EmitContext(), node) && - !transformers.IsLocalName(tx.EmitContext(), node) && - !isDeclarationNameOfEnumOrNamespace(tx.EmitContext(), node) { - exportContainer := tx.resolver.GetReferencedExportContainer(tx.EmitContext().MostOriginal(node), transformers.IsExportName(tx.EmitContext(), node)) - if exportContainer != nil && ast.IsSourceFile(exportContainer) { - reference := tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewIdentifier("exports"), - nil, /*questionDotToken*/ - node.Clone(tx.Factory()), - ast.NodeFlagsNone, - ) - tx.EmitContext().AssignCommentAndSourceMapRanges(reference, node) - reference.Loc = node.Loc - return reference - } - - importDeclaration := tx.resolver.GetReferencedImportDeclaration(tx.EmitContext().MostOriginal(node)) - if importDeclaration != nil { - if ast.IsImportClause(importDeclaration) { - reference := tx.Factory().NewPropertyAccessExpression( - tx.Factory().NewGeneratedNameForNode(importDeclaration.Parent), - nil, /*questionDotToken*/ - tx.Factory().NewIdentifier("default"), - ast.NodeFlagsNone, - ) - tx.EmitContext().AssignCommentAndSourceMapRanges(reference, node) - reference.Loc = node.Loc - return reference - } - if ast.IsImportSpecifier(importDeclaration) { - name := importDeclaration.AsImportSpecifier().PropertyNameOrName() - decl := ast.FindAncestor(importDeclaration, ast.IsImportDeclaration) - target := tx.Factory().NewGeneratedNameForNode(core.Coalesce(decl, importDeclaration)) - var reference *ast.Node - if ast.IsStringLiteral(name) { - reference = tx.Factory().NewElementAccessExpression( - target, - nil, /*questionDotToken*/ - tx.Factory().NewStringLiteralFromNode(name), - ast.NodeFlagsNone, - ) - } else { - referenceName := name.Clone(tx.Factory()) - tx.EmitContext().AddEmitFlags(referenceName, printer.EFNoSourceMap|printer.EFNoComments) - reference = tx.Factory().NewPropertyAccessExpression( - target, - nil, /*questionDotToken*/ - referenceName, - ast.NodeFlagsNone, - ) - } - tx.EmitContext().AssignCommentAndSourceMapRanges(reference, node) - reference.Loc = node.Loc - return reference - } - } - } - return node -} - -// Gets the exported names of an identifier, if it is exported. -func (tx *CommonJSModuleTransformer) getExports(name *ast.IdentifierNode) []*ast.ModuleExportName { - if !transformers.IsGeneratedIdentifier(tx.EmitContext(), name) { - importDeclaration := tx.resolver.GetReferencedImportDeclaration(tx.EmitContext().MostOriginal(name)) - if importDeclaration != nil { - return tx.currentModuleInfo.exportedBindings.Get(importDeclaration) - } - - // An exported namespace or enum may merge with an ambient declaration, which won't show up in .js emit, so - // we analyze all value exports of a symbol. - var bindingsSet collections.Set[*ast.ModuleExportName] - var bindings []*ast.ModuleExportName - declarations := tx.resolver.GetReferencedValueDeclarations(tx.EmitContext().MostOriginal(name)) - if declarations != nil { - for _, declaration := range declarations { - exportedBindings := tx.currentModuleInfo.exportedBindings.Get(declaration) - for _, binding := range exportedBindings { - if !bindingsSet.Has(binding) { - bindingsSet.Add(binding) - bindings = append(bindings, binding) - } - } - } - return bindings - } - } else if isFileLevelReservedGeneratedIdentifier(tx.EmitContext(), name) { - exportSpecifiers := tx.currentModuleInfo.exportSpecifiers.Get(name.Text()) - if exportSpecifiers != nil { - var exportedNames []*ast.ModuleExportName - for _, exportSpecifier := range exportSpecifiers { - exportedNames = append(exportedNames, exportSpecifier.Name()) - } - return exportedNames - } - } - return nil -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/commonjsmodule_test.go b/kitcom/internal/tsgo/transformers/moduletransforms/commonjsmodule_test.go deleted file mode 100644 index 0a822d7..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/commonjsmodule_test.go +++ /dev/null @@ -1,1057 +0,0 @@ -package moduletransforms_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/moduletransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" -) - -func fakeGetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind { - return core.ModuleKindNone -} - -func TestCommonJSModuleTransformer(t *testing.T) { - t.Parallel() - data := []struct { - title string - input string - output string - other string - jsx bool - options *core.CompilerOptions - }{ - // ImportDeclaration - { - title: "ImportDeclaration#1", - input: `import "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -require("other");`, - }, - { - title: "ImportDeclaration#2", - input: `import * as a from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const a = require("other");`, - }, - { - title: "ImportDeclaration#3", - input: `import { a } from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other");`, - }, - { - title: "ImportDeclaration#4", - input: `import a from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other");`, - }, - { - title: "ImportDeclaration#5", - input: `import a, * as b from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other"), b = other_1;`, - }, - { - title: "ImportDeclaration#6", - input: `import { a } from "other" -export { a }`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -const other_1 = require("other"); -Object.defineProperty(exports, "a", { enumerable: true, get: function () { return other_1.a; } });`, - }, - { - title: "ImportDeclaration#7", - input: `import * as a from "other"`, - output: `"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -const a = __importStar(require("other"));`, - options: &core.CompilerOptions{ESModuleInterop: core.TSTrue}, - }, - { - title: "ImportDeclaration#8", - input: `import * as a from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -const a = tslib_1.__importStar(require("other"));`, - options: &core.CompilerOptions{ESModuleInterop: core.TSTrue, ImportHelpers: core.TSTrue}, - }, - - // ImportEqualsDeclaration - { - title: "ImportEqualsDeclaration#1", - input: `import a = require("other");`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const a = require("other");`, - }, - { - title: "ImportEqualsDeclaration#2", - input: `export import a = require("other");`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = require("other");`, - }, - - // ExportDeclaration - { - title: "ExportDeclaration#1", - input: `export { a } from "other";`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -const other_1 = require("other"); -Object.defineProperty(exports, "a", { enumerable: true, get: function () { return other_1.a; } });`, - }, - { - title: "ExportDeclaration#2", - input: `export * as a from "other";`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -exports.a = require("other");`, - }, - { - title: "ExportDeclaration#3", - input: `export * from "other";`, - output: `"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("other"), exports);`, - }, - { - title: "ExportDeclaration#4", - input: `export * as a from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -const tslib_1 = require("tslib"); -exports.a = tslib_1.__importStar(require("other"));`, - options: &core.CompilerOptions{ESModuleInterop: core.TSTrue, ImportHelpers: core.TSTrue}, - }, - { - title: "ExportDeclaration#5", - input: `export * from "other"`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -tslib_1.__exportStar(require("other"), exports);`, - options: &core.CompilerOptions{ESModuleInterop: core.TSTrue, ImportHelpers: core.TSTrue}, - }, - - // ExportAssignment - { - title: "ExportAssignment#1", - input: `var a = 0; -export default a;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var a = 0; -exports.default = a;`, - }, - { - title: "ExportAssignment#2", - input: `var a = 0; -export = a;`, - output: `"use strict"; -var a = 0; -module.exports = a;`, - }, - - // FunctionDeclaration - { - title: "FunctionDeclaration#1", - input: `export function f() {}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.f = f; -function f() { }`, - }, - { - title: "FunctionDeclaration#2", - input: `export default function f() {}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = f; -function f() { }`, - }, - { - title: "FunctionDeclaration#3", - input: `export default function () {}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = default_1; -function default_1() { }`, - }, - { - title: "FunctionDeclaration#4", - input: `function f() {} -export { f };`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.f = f; -function f() { }`, - }, - - // ClassDeclaration - { - title: "ClassDeclaration#1", - input: `export class C {}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.C = void 0; -class C { -} -exports.C = C;`, - }, - { - title: "ClassDeclaration#2", - input: `export default class C {}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class C { -} -exports.default = C;`, - }, - { - title: "ClassDeclaration#3", - input: `export default class {}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class default_1 { -} -exports.default = default_1;`, - }, - { - title: "ClassDeclaration#4", - input: `class C {} -export { C };`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.C = void 0; -class C { -} -exports.C = C;`, - }, - - // VariableStatement - { - title: "VariableStatement#1", - input: `export var x = y;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -exports.x = y;`, - }, - { - title: "VariableStatement#2", - input: `export var { x } = y;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -({ x: exports.x } = y);`, - }, - { - title: "VariableStatement#3", - input: `export var [x] = y;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -[exports.x] = y;`, - }, - { - title: "VariableStatement#4", - input: `var x; -export { x }; -x || (x = 1);`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -x || (exports.x = x = 1);`, - }, - { - title: "VariableStatement#5 (from enum)", - input: `export enum E { A }`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.E = void 0; -var E; -(function (E) { - E[E["A"] = 0] = "A"; -})(E || (exports.E = E = {}));`, - }, - - // ForStatement - { - title: "ForStatement#1", - input: `export { x }; -for (var x = 0; ; ) ;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x = 0; -exports.x = x; -for (;;) - ;`, - }, - - // ForInStatement - { - title: "ForInStatement#1", - input: `export { x }; -for (var x in {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -for (var x in {}) { - exports.x = x; - ; -}`, - }, - - // ForOfStatement - { - title: "ForOfStatement#1", - input: `export { x }; -for (var x of {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -for (var x of {}) { - exports.x = x; - ; -}`, - }, - - // DoStatement - { - title: "DoStatement#1", - input: `export { x }; -do { - var x = 0; -} while (false);`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -do { - var x = 0; - exports.x = x; -} while (false);`, - }, - - // WhileStatement - { - title: "WhileStatement#1", - input: `export { x }; -while (true) { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -while (true) { - var x = 0; - exports.x = x; -}`, - }, - - // LabeledStatement - { - title: "LabeledStatement#1", - input: `export { x }; -label: { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -label: { - var x = 0; - exports.x = x; -}`, - }, - - // WithStatement - { - title: "WithStatement#1", - input: `export { x }; -with ({}) { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -with ({}) { - var x = 0; - exports.x = x; -}`, - }, - - // IfStatement - { - title: "IfStatement#1", - input: `export { x }; -if (y) { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -if (y) { - var x = 0; - exports.x = x; -}`, - }, - { - title: "IfStatement#2", - input: `export { x }; -if (y) { -} -else { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -if (y) { -} -else { - var x = 0; - exports.x = x; -}`, - }, - - // SwitchStatement - { - title: "SwitchStatement#1", - input: `export { x }; -switch (y) { - case 0: - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -switch (y) { - case 0: - var x = 0; - exports.x = x; -}`, - }, - { - title: "SwitchStatement#2", - input: `export { x }; -switch (y) { - default: - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -switch (y) { - default: - var x = 0; - exports.x = x; -}`, - }, - - // TryStatement - { - title: "TryStatement#1", - input: `export { x }; -try { - var x = 0; -} -catch { -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -try { - var x = 0; - exports.x = x; -} -catch { -}`, - }, - { - title: "TryStatement#2", - input: `export { x }; -try { -} -catch { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -try { -} -catch { - var x = 0; - exports.x = x; -}`, - }, - { - title: "TryStatement#3", - input: `export { x }; -try { -} -finally { - var x = 0; -}`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -try { -} -finally { - var x = 0; - exports.x = x; -}`, - }, - - // DestructuringAssignment - { - title: "DestructuringAssignment#1", - input: `var x; -export { x }; -({ x: x } = {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -({ x: { set value(value) { exports.x = x = value; } }.value } = {});`, - }, - { - title: "DestructuringAssignment#2", - input: `var x; -export { x }; -({ x: x = 1 } = {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -({ x: { set value(value) { exports.x = x = value; } }.value = 1 } = {});`, - }, - { - title: "DestructuringAssignment#3", - input: `var x; -export { x }; -({ x } = {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -({ x: { set value(value) { exports.x = x = value; } }.value } = {});`, - }, - { - title: "DestructuringAssignment#4", - input: `var x; -export { x }; -({ x = 1 } = {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -({ x: { set value(value) { exports.x = x = value; } }.value = 1 } = {});`, - }, - { - title: "DestructuringAssignment#5", - input: `var x; -export { x }; -({ ...x } = {});`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -({ ...{ set value(value) { exports.x = x = value; } }.value } = {});`, - }, - { - title: "DestructuringAssignment#6", - input: `var x; -export { x }; -[x] = [];`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -[{ set value(value) { exports.x = x = value; } }.value] = [];`, - }, - { - title: "DestructuringAssignment#7", - input: `var x; -export { x }; -[x = 1] = [];`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -[{ set value(value) { exports.x = x = value; } }.value = 1] = [];`, - }, - { - title: "DestructuringAssignment#8", - input: `var x; -export { x }; -[...x] = [];`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -[...{ set value(value) { exports.x = x = value; } }.value] = [];`, - }, - { - title: "DestructuringAssignment#9", - input: `var x; -export { x }; -[{ x: x }] = [];`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -var x; -[{ x: { set value(value) { exports.x = x = value; } }.value }] = [];`, - }, - - // AssignmentExpression - { - title: "AssignmentExpression#1", - input: `export var a; -a = 1;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -exports.a = 1;`, - }, - { - title: "AssignmentExpression#2", - input: `var a; -export { a }; -a = 1;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a; -exports.a = a = 1;`, - }, - { - title: "AssignmentExpression#3", - input: `var a; -export { a, a as b }; -a = 1;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.b = exports.a = void 0; -var a; -exports.b = exports.a = a = 1;`, - }, - - // PrefixUnaryExpression - { - title: "PrefixUnaryExpression#1", - input: `export var a = 0; -++a;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -exports.a = 0; -++exports.a;`, - }, - { - title: "PrefixUnaryExpression#2", - input: `var a = 0; -export { a } -++a;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0; -exports.a = a; -exports.a = ++a;`, - }, - - // PostfixUnaryExpression - { - title: "PostfixUnaryExpression#1", - input: `export var a = 0; -a++;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -exports.a = 0; -exports.a++;`, - }, - { - title: "PostfixUnaryExpression#2", - input: `var a = 0; -export { a } -a++;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0; -exports.a = a; -exports.a = (a++, a);`, - }, - { - title: "PostfixUnaryExpression#3", - input: `var a = 0, b; -export { a } -b = a++;`, - output: `"use strict"; -var _a; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0, b; -exports.a = a; -b = (exports.a = (_a = a++, a), _a);`, - }, - { - title: "PostfixUnaryExpression#4", - input: `var a = 0; -export { a } -(a++);`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0; -exports.a = a; -(exports.a = (a++, a));`, - }, - { - title: "PostfixUnaryExpression#5", - input: `var a = 0; -export { a } -a++, 0;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0; -exports.a = a; -exports.a = (a++, a), 0;`, - }, - { - title: "PostfixUnaryExpression#6", - input: `var a = 0, b; -export { a } -b = (a++, 0);`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0, b; -exports.a = a; -b = (exports.a = (a++, a), 0);`, - }, - { - title: "PostfixUnaryExpression#7", - input: `var a = 0, b; -export { a } -b = (0, a++);`, - output: `"use strict"; -var _a; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -var a = 0, b; -exports.a = a; -b = (0, exports.a = (_a = a++, a), _a);`, - }, - - // ShortHandPropertyAssignment - { - title: "ShorthandPropertyAssignment#1", - input: `import { a } from "other" -({ a })`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other"); -({ a: other_1.a });`, - }, - { - title: "ShorthandPropertyAssignment#2", - input: `import { a } from "other" -({ - a, -})`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other"); -({ - a: other_1.a, -});`, - }, - - // CallExpression - { - title: "CallExpression#1", - input: `import { a } from "other" -a()`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other"); -(0, other_1.a)();`, - }, - { - title: "CallExpression#2", - input: `export var a = (0, function() {}); -a()`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -exports.a = (0, function () { }); -(0, exports.a)();`, - }, - { - title: "CallExpression#3", - input: `export{}; -import("./other.ts");`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -Promise.resolve().then(() => require("./other.js"));`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "CallExpression#4", - input: `export{}; -import(x);`, - output: `"use strict"; -var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { - if (typeof path === "string" && /^\.\.?\//.test(path)) { - return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { - return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); - }); - } - return path; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -Promise.resolve(` + "`" + `${__rewriteRelativeImportExtension(x)}` + "`" + `).then(s => require(s));`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "CallExpression#5", - input: `export{}; -import(x);`, - output: `"use strict"; -var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { - if (typeof path === "string" && /^\.\.?\//.test(path)) { - return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { - return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); - }); - } - return path; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -Promise.resolve(` + "`" + `${__rewriteRelativeImportExtension(x, true)}` + "`" + `).then(s => require(s));`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue, Jsx: core.JsxEmitPreserve}, - }, - { - title: "CallExpression#6", - input: `export{}; -import(x);`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -Promise.resolve(` + "`" + `${tslib_1.__rewriteRelativeImportExtension(x)}` + "`" + `).then(s => require(s));`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue, ImportHelpers: core.TSTrue}, - }, - { - title: "CallExpression#7", - input: `export {}; -a?.()`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -a?.();`, - }, - - // TaggedTemplateExpression - { - title: "TaggedTemplateExpression#1", - input: "import { a } from \"other\"\n" + - "a``", - output: "\"use strict\";\n" + - "Object.defineProperty(exports, \"__esModule\", { value: true });\n" + - "const other_1 = require(\"other\");\n" + - "(0, other_1.a) ``;", - }, - { - title: "TaggedTemplateExpression#1", - input: "export var a = (0, function() {});" + - "a``", - output: "\"use strict\";\n" + - "Object.defineProperty(exports, \"__esModule\", { value: true });\n" + - "exports.a = void 0;\n" + - "exports.a = (0, function () { });\n" + - "(0, exports.a) ``;", - }, - - // Identifier - { - title: "Identifier#1", - input: `import { a } from "other" -a;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other"); -other_1.a;`, - }, - { - title: "Identifier#2", - input: `export var a = 0; -a;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -exports.a = 0; -exports.a;`, - }, - { - title: "Identifier#3 (from enum)", - input: `export enum E { A } -E.A`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.E = void 0; -var E; -(function (E) { - E[E["A"] = 0] = "A"; -})(E || (exports.E = E = {})); -E.A;`, - }, - { - title: "Identifier#4 (preserve location)", - input: `import { a } from "other"; -x || - a`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const other_1 = require("other"); -x || - other_1.a;`, - }, - { - title: "Identifier#5 (from import specifier)", - input: `import { and } from "./_namespaces/ts.js"; -const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor); -`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const ts_js_1 = require("./_namespaces/ts.js"); -const isNotOverloadAndNotAccessor = (0, ts_js_1.and)(isNotOverload, isNotAccessor);`, - }, - - { - title: "Identifier#6 (in template literal)", - input: `export var x = 1; -` + "`" + `${x}` + "`" + `;`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.x = void 0; -exports.x = 1; -` + "`" + `${exports.x}` + "`" + `;`, - }, - - { - title: "Other", - input: `export const a = class { - p = 10; -};`, - output: `"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.a = void 0; -const a = class { - p = 10; -}; -exports.a = a;`, - }, - } - for _, rec := range data { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - - compilerOptions := rec.options - if compilerOptions == nil { - compilerOptions = &core.CompilerOptions{} - } - - compilerOptions.Module = core.ModuleKindCommonJS - - file := parsetestutil.ParseTypeScript(rec.input, rec.jsx) - parsetestutil.CheckDiagnostics(t, file) - binder.BindSourceFile(file) - - var other *ast.SourceFile - if len(rec.other) > 0 { - other = parsetestutil.ParseTypeScript(rec.other, rec.jsx) - parsetestutil.CheckDiagnostics(t, other) - binder.BindSourceFile(other) - } - - emitContext := printer.NewEmitContext() - resolver := binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) - opts := transformers.TransformOptions{CompilerOptions: compilerOptions, Context: emitContext, Resolver: resolver, GetEmitModuleFormatOfFile: fakeGetEmitModuleFormatOfFile} - file = tstransforms.NewRuntimeSyntaxTransformer(&opts).TransformSourceFile(file) - file = moduletransforms.NewCommonJSModuleTransformer(&opts).TransformSourceFile(file) - emittestutil.CheckEmit(t, emitContext, file, rec.output) - }) - } -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/esmodule.go b/kitcom/internal/tsgo/transformers/moduletransforms/esmodule.go deleted file mode 100644 index 1eabcc1..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/esmodule.go +++ /dev/null @@ -1,370 +0,0 @@ -package moduletransforms - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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 - resolver := opts.Resolver - if resolver == nil { - resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) - } - tx := &ESModuleTransformer{compilerOptions: compilerOptions, resolver: 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) - statements := slices.Clone(prologue) - if externalHelpersImportDeclaration != nil { - statements = append(statements, 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( - ast.NodeFlagsConst, - tx.Factory().NewNodeList([]*ast.Node{ - tx.Factory().NewVariableDeclaration( - node.Name().Clone(tx.Factory()), - nil, /*exclamationToken*/ - nil, /*type*/ - tx.createRequireCall(node.AsNode()), - ), - }), - ), - ) - 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, - ) -} - -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"), - 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( - ast.NodeFlagsConst, - 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, - ), - ), - }), - ), - ) - 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, - ) -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/esmodule_test.go b/kitcom/internal/tsgo/transformers/moduletransforms/esmodule_test.go deleted file mode 100644 index 01a6d60..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/esmodule_test.go +++ /dev/null @@ -1,249 +0,0 @@ -package moduletransforms_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/moduletransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" -) - -func TestESModuleTransformer(t *testing.T) { - t.Parallel() - data := []struct { - title string - input string - output string - other string - jsx bool - options *core.CompilerOptions - }{ - // ImportDeclaration - { - title: "ImportDeclaration#1", - input: `import "other"`, - output: `import "other";`, - }, - { - title: "ImportDeclaration#2", - input: `import "./other.ts"`, - output: `import "./other.js";`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "ImportDeclaration#3", - input: `import "./other.tsx"`, - output: `import "./other.js";`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "ImportDeclaration#4", - input: `import "./other.tsx"`, - output: `import "./other.jsx";`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue, Jsx: core.JsxEmitPreserve}, - }, - - // ImportEqualsDeclaration - { - title: "ImportEqualsDeclaration#1", - input: `import x = require("other")`, - output: `export {};`, - }, - { - title: "ImportEqualsDeclaration#2", - input: `import x = require("other")`, - output: `import { createRequire as _createRequire } from "module"; -const __require = _createRequire(import.meta.url); -const x = __require("other");`, - options: &core.CompilerOptions{Module: core.ModuleKindNode16}, - }, - { - title: "ImportEqualsDeclaration#3", - input: `import x = require("./other.ts")`, - output: `import { createRequire as _createRequire } from "module"; -const __require = _createRequire(import.meta.url); -const x = __require("./other.js");`, - options: &core.CompilerOptions{Module: core.ModuleKindNode16, RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "ImportEqualsDeclaration#4", - input: `import x = require("./other.tsx")`, - output: `import { createRequire as _createRequire } from "module"; -const __require = _createRequire(import.meta.url); -const x = __require("./other.js");`, - options: &core.CompilerOptions{Module: core.ModuleKindNode16, RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "ImportEqualsDeclaration#5", - input: `import x = require("./other.tsx")`, - output: `import { createRequire as _createRequire } from "module"; -const __require = _createRequire(import.meta.url); -const x = __require("./other.jsx");`, - options: &core.CompilerOptions{Module: core.ModuleKindNode16, RewriteRelativeImportExtensions: core.TSTrue, Jsx: core.JsxEmitPreserve}, - }, - { - title: "ImportEqualsDeclaration#6", - input: `export import x = require("other")`, - output: `import { createRequire as _createRequire } from "module"; -const __require = _createRequire(import.meta.url); -const x = __require("other"); -export { x };`, - options: &core.CompilerOptions{Module: core.ModuleKindNode16}, - }, - - // ExportAssignment - { - title: "ExportAssignment#1", - input: `export = x`, - output: `export {};`, - }, - { - title: "ExportAssignment#2", - input: `export = x`, - output: `module.exports = x;`, - options: &core.CompilerOptions{Module: core.ModuleKindPreserve}, - }, - - // ExportDeclaration - { - title: "ExportDeclaration#1", - input: `export * from "other";`, - output: `export * from "other";`, - }, - { - title: "ExportDeclaration#2", - input: `export * from "./other.ts";`, - output: `export * from "./other.js";`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "ExportDeclaration#3", - input: `export * as x from "other";`, - output: `export * as x from "other";`, - options: &core.CompilerOptions{Module: core.ModuleKindESNext}, - }, - { - title: "ExportDeclaration#4", - input: `export { x } from "other";`, - output: `export { x } from "other";`, - }, - { - title: "ExportDeclaration#5", - input: `export * as x from "other";`, - output: `import * as x_1 from "other"; -export { x_1 as x };`, - }, - { - title: "ExportDeclaration#6", - input: `export * as default from "other";`, - output: `import * as default_1 from "other"; -export default default_1;`, - }, - - // CallExpression - { - title: "CallExpression#1", - input: `import("other");`, - output: `import("other");`, - }, - { - title: "CallExpression#2", - input: `import(x);`, - output: `import(x);`, - }, - { - title: "CallExpression#3", - input: `export {}; -import("./other.ts");`, - output: `export {}; -import("./other.js");`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "CallExpression#4", - input: `export {}; -import(x);`, - output: `var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { - if (typeof path === "string" && /^\.\.?\//.test(path)) { - return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { - return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); - }); - } - return path; -}; -export {}; -import(__rewriteRelativeImportExtension(x));`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue}, - }, - { - title: "CallExpression#5", - input: `export {}; -import(x);`, - output: `var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { - if (typeof path === "string" && /^\.\.?\//.test(path)) { - return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { - return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); - }); - } - return path; -}; -export {}; -import(__rewriteRelativeImportExtension(x, true));`, - options: &core.CompilerOptions{RewriteRelativeImportExtensions: core.TSTrue, Jsx: core.JsxEmitPreserve}, - }, - { - title: "CallExpression#6", - input: `export {}; -import(x);`, - output: `import { __rewriteRelativeImportExtension } from "tslib"; -export {}; -import(__rewriteRelativeImportExtension(x));`, - options: &core.CompilerOptions{Module: core.ModuleKindESNext, RewriteRelativeImportExtensions: core.TSTrue, ImportHelpers: core.TSTrue}, - }, - { - title: "CallExpression#7", - input: `export {}; -import(x); -var __rewriteRelativeImportExtension;`, - output: `import { __rewriteRelativeImportExtension as __rewriteRelativeImportExtension_1 } from "tslib"; -export {}; -import(__rewriteRelativeImportExtension_1(x)); -var __rewriteRelativeImportExtension;`, - options: &core.CompilerOptions{Module: core.ModuleKindESNext, RewriteRelativeImportExtensions: core.TSTrue, ImportHelpers: core.TSTrue}, - }, - } - for _, rec := range data { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - - compilerOptions := rec.options - if compilerOptions == nil { - compilerOptions = &core.CompilerOptions{} - } - - file := parsetestutil.ParseTypeScript(rec.input, rec.jsx) - parsetestutil.CheckDiagnostics(t, file) - binder.BindSourceFile(file) - - var other *ast.SourceFile - if len(rec.other) > 0 { - other = parsetestutil.ParseTypeScript(rec.other, rec.jsx) - parsetestutil.CheckDiagnostics(t, other) - binder.BindSourceFile(other) - } - - emitContext := printer.NewEmitContext() - resolver := binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) - opts := transformers.TransformOptions{CompilerOptions: compilerOptions, Context: emitContext, Resolver: resolver, GetEmitModuleFormatOfFile: fakeGetEmitModuleFormatOfFile} - file = tstransforms.NewRuntimeSyntaxTransformer(&opts).TransformSourceFile(file) - file = moduletransforms.NewESModuleTransformer(&opts).TransformSourceFile(file) - emittestutil.CheckEmit(t, emitContext, file, rec.output) - }) - } -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/externalmoduleinfo.go b/kitcom/internal/tsgo/transformers/moduletransforms/externalmoduleinfo.go deleted file mode 100644 index ef3a7a9..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/externalmoduleinfo.go +++ /dev/null @@ -1,390 +0,0 @@ -package moduletransforms - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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 { - 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.AsNamedExports().Elements.Nodes { - 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().AsBindingPattern().Elements.Nodes { - 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)), - ) - emitContext.AddEmitFlags(externalHelpersImportDeclaration, printer.EFNeverApplyImportHelper|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), - nil, /*attributes*/ - ) - - emitContext.AddEmitFlags(externalHelpersImportDeclaration, printer.EFNeverApplyImportHelper|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 || compilerOptions.GetESModuleInterop() && 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 { - if node == nil { - return false - } - var elements *ast.NodeList - switch { - case ast.IsNamedImports(node): - elements = node.AsNamedImports().Elements - case ast.IsNamedExports(node): - elements = node.AsNamedExports().Elements - default: - return false - } - return core.Some(elements.Nodes, 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))) -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/impliedmodule.go b/kitcom/internal/tsgo/transformers/moduletransforms/impliedmodule.go deleted file mode 100644 index db4409a..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/impliedmodule.go +++ /dev/null @@ -1,58 +0,0 @@ -package moduletransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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 { - compilerOptions := opts.CompilerOptions - resolver := opts.Resolver - if resolver == nil { - resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) - } - tx := &ImpliedModuleTransformer{opts: opts, resolver: 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() -} diff --git a/kitcom/internal/tsgo/transformers/moduletransforms/utilities.go b/kitcom/internal/tsgo/transformers/moduletransforms/utilities.go deleted file mode 100644 index fb09b9a..0000000 --- a/kitcom/internal/tsgo/transformers/moduletransforms/utilities.go +++ /dev/null @@ -1,119 +0,0 @@ -package moduletransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/outputpaths" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -func isDeclarationNameOfEnumOrNamespace(emitContext *printer.EmitContext, node *ast.IdentifierNode) bool { - if original := emitContext.MostOriginal(node); original != nil && original.Parent != nil { - switch original.Parent.Kind { - case ast.KindEnumDeclaration, ast.KindModuleDeclaration: - return original == original.Parent.Name() - } - } - 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) - // !!! set quote style - 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 /// -// 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 { - name = factory.NewStringLiteral(moduleName.Text()) - } - 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 /// -// 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) -} diff --git a/kitcom/internal/tsgo/transformers/transformer.go b/kitcom/internal/tsgo/transformers/transformer.go deleted file mode 100644 index f0539c8..0000000 --- a/kitcom/internal/tsgo/transformers/transformer.go +++ /dev/null @@ -1,41 +0,0 @@ -package transformers - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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) -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/importelision.go b/kitcom/internal/tsgo/transformers/tstransforms/importelision.go deleted file mode 100644 index ca20c7f..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/importelision.go +++ /dev/null @@ -1,205 +0,0 @@ -package tstransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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 { - switch node.Kind { - case ast.KindImportEqualsDeclaration: - if !tx.isElisionBlocked(node) && !tx.shouldEmitImportEqualsDeclaration(node.AsImportEqualsDeclaration()) { - return nil - } - return tx.Visitor().VisitEachChild(node) - case ast.KindImportDeclaration: - if !tx.isElisionBlocked(node) { - 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.isElisionBlocked(node) && !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && !tx.isValueAliasDeclaration(node) { - // elide unused import - return nil - } - return tx.Visitor().VisitEachChild(node) - case ast.KindExportDeclaration: - if !tx.isElisionBlocked(node) { - 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)) - } - return tx.Visitor().VisitEachChild(node) - 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 - 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 { - if !tx.shouldEmitAliasDeclaration(node.AsNode()) { - return false - } - if node.ModuleReference.Kind == ast.KindExternalModuleReference { - return true - } - // 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.currentSourceFile != nil && 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) -} - -// Determines whether import/export elision is blocked for this statement. -// -// @description -// We generally block import/export elision if the statement was modified by a `before` custom -// transform, although we will continue to allow it if the statement hasn't replaced a node of a different kind and -// as long as the local bindings for the declarations are unchanged. -func (tx *ImportElisionTransformer) isElisionBlocked(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration*/) bool { - parsed := tx.EmitContext().ParseNode(node) - if parsed == node || ast.IsExportAssignment(node) { - return false - } - - if parsed == nil || parsed.Kind != node.Kind { - // no longer safe to elide as the declaration was replaced with a node of a different kind - return true - } - - switch node.Kind { - case ast.KindImportDeclaration: - n := node.AsImportDeclaration() - p := parsed.AsImportDeclaration() - if n.ImportClause != p.ImportClause { - return true // no longer safe to elide as the import clause has changed - } - if n.Attributes != p.Attributes { - return true // no longer safe to elide as the import attributes have changed - } - case ast.KindImportEqualsDeclaration: - n := node.AsImportEqualsDeclaration() - p := parsed.AsImportEqualsDeclaration() - if n.Name() != p.Name() { - return true // no longer safe to elide as local binding has changed - } - if n.IsTypeOnly != p.IsTypeOnly { - return true // no longer safe to elide as `type` modifier has changed - } - if n.ModuleReference != p.ModuleReference && (ast.IsEntityName(n.ModuleReference) || ast.IsEntityName(p.ModuleReference)) { - return true // no longer safe to elide as EntityName reference has changed. - } - case ast.KindExportDeclaration: - n := node.AsExportDeclaration() - p := parsed.AsExportDeclaration() - if n.ExportClause != p.ExportClause { - return true // no longer safe to elide as the export clause has changed - } - if n.Attributes != p.Attributes { - return true // no longer safe to elide as the export attributes have changed - } - } - - return false -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/importelision_test.go b/kitcom/internal/tsgo/transformers/tstransforms/importelision_test.go deleted file mode 100644 index f002242..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/importelision_test.go +++ /dev/null @@ -1,258 +0,0 @@ -package tstransforms_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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) GetPackageJsonInfo(pkgJsonPath string) modulespecifiers.PackageJsonInfo { - 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) 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 - }, - }) - - emitResolver := c.GetEmitResolver() - emitResolver.MarkLinkedReferencesRecursively(file) - - 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) - }) - } -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/runtimesyntax.go b/kitcom/internal/tsgo/transformers/tstransforms/runtimesyntax.go deleted file mode 100644 index 365ad10..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/runtimesyntax.go +++ /dev/null @@ -1,1159 +0,0 @@ -package tstransforms - -// !!! Unqualified enum member references across merged enum declarations are not currently supported (e.g `enum E {A}; enum E {B=A}`) -// !!! Unqualified namespace member references across merged namespace declarations are not currently supported (e.g `namespace N { export var x = 1; }; namespace N { x; }`). -// !!! SourceMaps and Comments need to be validated - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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 - evaluator evaluator.Evaluator - enumMemberCache map[*ast.EnumDeclarationNode]map[string]evaluator.Result -} - -func NewRuntimeSyntaxTransformer(opt *transformers.TransformOptions) *transformers.Transformer { - compilerOptions := opt.CompilerOptions - emitContext := opt.Context - tx := &RuntimeSyntaxTransformer{compilerOptions: compilerOptions, resolver: opt.Resolver} - 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.KindImportEqualsDeclaration: - 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.AsBindingPattern().Elements.Nodes { - 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 && node.ModifierFlags()&ast.ModifierFlagsExport != 0 -} - -func (tx *RuntimeSyntaxTransformer) isExportOfExternalModule(node *ast.Node) bool { - return tx.currentNamespace == nil && 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().NewStringLiteralFromNode(name) - case ast.KindStringLiteral: - return tx.Factory().NewStringLiteral(name.AsStringLiteral().Text) - case ast.KindNumericLiteral: - return tx.Factory().NewNumericLiteral(name.AsNumericLiteral().Text) - default: - return name - } -} - -// Gets an expression like `E.A` or `E["A"]` that references an enum member. -func (tx *RuntimeSyntaxTransformer) getEnumQualifiedReference(enum *ast.EnumDeclaration, member *ast.EnumMember) *ast.Expression { - if ast.IsIdentifier(member.Name()) { - return tx.getEnumQualifiedProperty(enum, member) - } else { - return tx.getEnumQualifiedElement(enum, member) - } -} - -// Gets an expression like `E.A` that references an enum member. -func (tx *RuntimeSyntaxTransformer) getEnumQualifiedProperty(enum *ast.EnumDeclaration, member *ast.EnumMember) *ast.Expression { - prop := tx.getNamespaceQualifiedProperty(tx.getNamespaceContainerName(enum.AsNode()), member.Name().Clone(tx.Factory())) - tx.EmitContext().AddEmitFlags(prop, printer.EFNoComments|printer.EFNoNestedComments|printer.EFNoSourceMap|printer.EFNoNestedSourceMaps) - return prop -} - -// 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 { - exportName := tx.Factory().GetDeclarationNameEx(node.AsNode(), printer.NameOptions{AllowSourceMaps: true}) - if tx.isExportOfNamespace(node.AsNode()) { - return tx.getNamespaceQualifiedProperty(tx.getNamespaceContainerName(tx.currentNamespace), exportName) - } - return exportName -} - -func (tx *RuntimeSyntaxTransformer) addVarForDeclaration(statements []*ast.Statement, node *ast.Declaration) ([]*ast.Statement, bool) { - tx.recordDeclarationInScope(node) - if !tx.isFirstDeclarationInScope(node) { - return statements, false - } - - if tx.isExportOfExternalModule(node) { - // export { name }; - 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*/ - )) - } - - // 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(varFlags, tx.Factory().NewNodeList([]*ast.Node{varDecl})) - varStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, varDecls) - - tx.EmitContext().SetOriginal(varDecl, node) - // !!! synthetic comments - tx.EmitContext().SetOriginal(varStatement, node) - - // Adjust the source map emit to match the old emitter. - tx.EmitContext().SetSourceMapRange(varDecls, 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{} - if len(node.Members.Nodes) > 0 { - tx.EmitContext().StartVariableEnvironment() - - var autoValue jsnum.Number - var autoVar *ast.IdentifierNode - var useAutoVar bool - for i := range len(node.Members.Nodes) { - // E[E["A"] = 0] = "A"; - statements = tx.transformEnumMember( - statements, - node, - i, - &autoValue, - &autoVar, - &useAutoVar, - ) - autoValue++ - } - - statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements) - } - - 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, - autoValue *jsnum.Number, - autoVar **ast.IdentifierNode, - useAutoVar *bool, -) []*ast.Statement { - memberNode := enum.Members.Nodes[index] - member := memberNode.AsEnumMember() - - var memberName string - if ast.IsIdentifier(member.Name()) || ast.IsStringLiteralLike(member.Name()) { - memberName = member.Name().Text() - } - - savedParent := tx.parentNode - tx.parentNode = tx.currentNode - tx.currentNode = memberNode - - // E[E["A"] = x] = "A"; - // ^ - expression := member.Initializer // NOTE: already visited - - var useConditionalReverseMapping bool - var useExplicitReverseMapping bool - if expression == nil { - // Enum members without an initializer are auto-numbered. We will use constant values if there was no preceding - // initialized member, or if the preceding initialized member was a numeric literal. - if *useAutoVar { - // If you are using an auto-numbered member following a non-numeric literal, we assume the previous member - // produced a valid numeric value. This assumption is intended to be validated by the type checker prior to - // emit. - // E[E["A"] = ++auto] = "A"; - // ^^^^^^ - expression = tx.Factory().NewPrefixUnaryExpression(ast.KindPlusPlusToken, *autoVar) - useExplicitReverseMapping = true - } else { - // If the preceding auto value is a finite number, we can emit a numeric literal for the member initializer: - // E[E["A"] = 0] = "A"; - // ^ - // If not, we cannot emit a valid numeric literal for the member initializer and emit `void 0` instead: - // E["A"] = void 0; - // ^^^^^^ - expression = constantExpression(*autoValue, tx.Factory()) - if expression != nil { - useExplicitReverseMapping = true - if len(memberName) > 0 { - tx.cacheEnumMemberValue(enum.AsNode(), memberName, evaluator.NewResult(*autoValue, false, false, false)) - } - } else { - expression = tx.Factory().NewVoidZeroExpression() - } - } - } else { - // Enum members with an initializer may restore auto-numbering if the initializer is a numeric literal. If we - // cannot syntactically determine the initializer value and the following enum member is auto-numbered, we will - // use an `auto` variable to perform the remaining auto-numbering at runtime. - if tx.evaluator == nil { - tx.evaluator = evaluator.NewEvaluator(tx.evaluateEntity, ast.OEKAll) - } - - var hasNumericInitializer, hasStringInitializer bool - result := tx.evaluator(expression, enum.AsNode()) - switch value := result.Value.(type) { - case jsnum.Number: - hasNumericInitializer = true - *autoValue = value - expression = core.Coalesce(constantExpression(value, tx.Factory()), expression) // TODO: preserve original expression after Strada migration - tx.cacheEnumMemberValue(enum.AsNode(), memberName, result) - case string: - hasStringInitializer = true - *autoValue = jsnum.NaN() - expression = core.Coalesce(constantExpression(value, tx.Factory()), expression) // TODO: preserve original expression after Strada migration - tx.cacheEnumMemberValue(enum.AsNode(), memberName, result) - default: - *autoValue = jsnum.NaN() - } - - nextIsAuto := index+1 < len(enum.Members.Nodes) && enum.Members.Nodes[index+1].AsEnumMember().Initializer == nil - useExplicitReverseMapping = hasNumericInitializer || !hasStringInitializer && nextIsAuto - useConditionalReverseMapping = !hasNumericInitializer && !hasStringInitializer && !nextIsAuto - if *useAutoVar = nextIsAuto && !hasNumericInitializer && !hasStringInitializer; *useAutoVar { - // E[E["A"] = auto = x] = "A"; - // ^^^^^^^^ - if *autoVar == nil { - *autoVar = tx.Factory().NewUniqueNameEx("auto", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) - tx.EmitContext().AddVariableDeclaration(*autoVar) - } - expression = tx.Factory().NewAssignmentExpression(*autoVar, expression) - } - } - - // Define the enum member property: - // E[E["A"] = ++auto] = "A"; - // ^^^^^^^^--_____ - expression = tx.Factory().NewAssignmentExpression( - tx.getEnumQualifiedElement(enum, member), - expression, - ) - - // If this is syntactically a numeric literal initializer, or is auto numbered, then we unconditionally define the - // reverse mapping for the enum member. - if useExplicitReverseMapping { - // E[E["A"] = A = ++auto] = "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) - - // If this is not auto numbered and is not syntactically a string or numeric literal initializer, then we - // conditionally define the reverse mapping for the enum member. - if useConditionalReverseMapping { - // E["A"] = x; - // if (typeof E.A !== "string") E.A = "A"; - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - ifStatement := tx.Factory().NewIfStatement( - tx.Factory().NewStrictInequalityExpression( - tx.Factory().NewTypeOfExpression(tx.getEnumQualifiedReference(enum, member)), - tx.Factory().NewStringLiteral("string"), - ), - tx.Factory().NewExpressionStatement( - tx.Factory().NewAssignmentExpression( - tx.Factory().NewElementAccessExpression( - tx.getNamespaceContainerName(enum.AsNode()), - nil, /*questionDotToken*/ - tx.getEnumQualifiedReference(enum, member), - ast.NodeFlagsNone, - ), - tx.getExpressionForPropertyName(member), - ), - ), - nil, - ) - - tx.EmitContext().AddEmitFlags(ifStatement, printer.EFSingleLine) - tx.EmitContext().AssignSourceMapRange(ifStatement, member.Initializer) - statements = append(statements, ifStatement) - } - - 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 - 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 := convertEntityNameToExpression(tx.EmitContext(), node.ModuleReference) - tx.EmitContext().SetEmitFlags(moduleReference, printer.EFNoComments|printer.EFNoNestedSourceMaps) - 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(ast.NodeFlagsNone, tx.Factory().NewNodeList([]*ast.Node{varDecl})) - 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}; - return tx.createExportStatement(node.Name(), moduleReference, node.Loc, node.Loc, node.AsNode()) - } -} - -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 { - expression := transformers.ConvertVariableDeclarationToAssignmentExpression(tx.EmitContext(), declaration.AsVariableDeclaration()) - 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()) -} - -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.ModifierFlagsExportDefault)), - 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()) - 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()) - tx.EmitContext().AddEmitFlags(propertyName, printer.EFNoComments|printer.EFNoSourceMap) - - localName := parameter.Name().Clone(tx.Factory()) - 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) - } - } - - var superPath []int - if ast.IsClassLike(grandparentOfBody) && ast.GetExtendsHeritageClauseElement(grandparentOfBody) != nil { - superPath = 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 -} - -// finds a path to a statement containing a `super` call, descending through `try` blocks -func findSuperStatementIndexPath(statements []*ast.Statement, start int) []int { - for i := start; i < len(statements); i++ { - statement := statements[i] - if getSuperCallFromStatement(statement) != nil { - indices := make([]int, 1, 2) - indices[0] = i - return indices - } else if ast.IsTryStatement(statement) { - return slices.Insert(findSuperStatementIndexPath(statement.AsTryStatement().TryBlock.AsBlock().Statements.Nodes, 0), 0, i) - } - } - return nil -} - -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 -} - -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), - 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()) - if tx.resolver == nil { - tx.resolver = binder.NewReferenceResolver(tx.compilerOptions, binder.ReferenceResolverHooks{}) - } - container := tx.resolver.GetReferencedExportContainer(location, false /*prefixLocals*/) - if container != nil && (ast.IsEnumDeclaration(container) || ast.IsModuleDeclaration(container)) && container.Contains(location) { - 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 { - name := node.Name() - if name == nil { - return nil - } - - localName := tx.Factory().GetLocalName(node) - exportAssignmentSourceMapRange := node.Loc - if node.Name() != nil { - exportAssignmentSourceMapRange = exportAssignmentSourceMapRange.WithPos(name.Pos()) - } - exportStatementSourceMapRange := node.Loc.WithPos(-1) - return tx.createExportStatement(name, localName, exportAssignmentSourceMapRange, exportStatementSourceMapRange, node) -} - -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) cacheEnumMemberValue(enum *ast.EnumDeclarationNode, memberName string, result evaluator.Result) { - if tx.enumMemberCache == nil { - tx.enumMemberCache = make(map[*ast.EnumDeclarationNode]map[string]evaluator.Result) - } - memberCache := tx.enumMemberCache[enum] - if memberCache == nil { - memberCache = make(map[string]evaluator.Result) - tx.enumMemberCache[enum] = memberCache - } - memberCache[memberName] = result -} - -func (tx *RuntimeSyntaxTransformer) isReferenceToEnum(reference *ast.IdentifierNode, enum *ast.EnumDeclarationNode) bool { - if transformers.IsGeneratedIdentifier(tx.EmitContext(), reference) { - originalEnum := tx.EmitContext().MostOriginal(enum) - return tx.EmitContext().GetNodeForGeneratedName(reference) == originalEnum - } - return reference.Text() == enum.Name().Text() -} - -func (tx *RuntimeSyntaxTransformer) evaluateEntity(node *ast.Node, location *ast.Node) evaluator.Result { - var result evaluator.Result - if ast.IsEnumDeclaration(location) { - memberCache := tx.enumMemberCache[location] - if memberCache != nil { - switch { - case ast.IsIdentifier(node): - result = memberCache[node.Text()] - case ast.IsPropertyAccessExpression(node): - access := node.AsPropertyAccessExpression() - expression := access.Expression - if ast.IsIdentifier(expression) && tx.isReferenceToEnum(expression, location) { - result = memberCache[access.Name().Text()] - } - case ast.IsElementAccessExpression(node): - access := node.AsElementAccessExpression() - expression := access.Expression - if ast.IsIdentifier(expression) && tx.isReferenceToEnum(expression, location) && ast.IsStringLiteralLike(access.ArgumentExpression) { - result = memberCache[access.ArgumentExpression.Text()] - } - } - } - } - return result -} - -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 isInstantiatedModule(node.AsNode(), 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 -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/runtimesyntax_test.go b/kitcom/internal/tsgo/transformers/tstransforms/runtimesyntax_test.go deleted file mode 100644 index d9c1218..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/runtimesyntax_test.go +++ /dev/null @@ -1,445 +0,0 @@ -package tstransforms_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" -) - -func TestEnumTransformer(t *testing.T) { - t.Parallel() - data := []struct { - title string - input string - output string - }{ - {title: "empty enum", input: "enum E {}", output: `var E; -(function (E) { -})(E || (E = {}));`}, - - {title: "simple enum", input: "enum E {A}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; -})(E || (E = {}));`}, - - {title: "autonumber enum #1", input: "enum E {A,B}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 1] = "B"; -})(E || (E = {}));`}, - - {title: "autonumber enum #2", input: "enum E {A = 1,B}", output: `var E; -(function (E) { - E[E["A"] = 1] = "A"; - E[E["B"] = 2] = "B"; -})(E || (E = {}));`}, - - {title: "autonumber enum #3", input: "enum E {A = 1,B,C}", output: `var E; -(function (E) { - E[E["A"] = 1] = "A"; - E[E["B"] = 2] = "B"; - E[E["C"] = 3] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #4", input: "enum E {A = x,B,C}", output: `var E; -(function (E) { - var auto; - E[E["A"] = auto = x] = "A"; - E[E["B"] = ++auto] = "B"; - E[E["C"] = ++auto] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #5", input: "enum E {A = x,B,C = y}", output: `var E; -(function (E) { - var auto; - E[E["A"] = auto = x] = "A"; - E[E["B"] = ++auto] = "B"; - E["C"] = y; - if (typeof E.C !== "string") E[E.C] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #6", input: "enum E {A = x,B = y,C = z}", output: `var E; -(function (E) { - E["A"] = x; - if (typeof E.A !== "string") E[E.A] = "A"; - E["B"] = y; - if (typeof E.B !== "string") E[E.B] = "B"; - E["C"] = z; - if (typeof E.C !== "string") E[E.C] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #7", input: "enum E {A = 1,B,C,D='x'}", output: `var E; -(function (E) { - E[E["A"] = 1] = "A"; - E[E["B"] = 2] = "B"; - E[E["C"] = 3] = "C"; - E["D"] = "x"; -})(E || (E = {}));`}, - - {title: "autonumber enum #8", input: "enum E {A,B=2,C}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 2] = "B"; - E[E["C"] = 3] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #9", input: "enum E {A='x',B=2,C}", output: `var E; -(function (E) { - E["A"] = "x"; - E[E["B"] = 2] = "B"; - E[E["C"] = 3] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #10", input: "enum E {A='x',B=y,C}", output: `var E; -(function (E) { - var auto; - E["A"] = "x"; - E[E["B"] = auto = y] = "B"; - E[E["C"] = ++auto] = "C"; -})(E || (E = {}));`}, - - {title: "autonumber enum #11", input: "enum E {A='x',B=1,C,D=y,E,F=3,G}", output: `var E; -(function (E) { - var auto; - E["A"] = "x"; - E[E["B"] = 1] = "B"; - E[E["C"] = 2] = "C"; - E[E["D"] = auto = y] = "D"; - E[E["E"] = ++auto] = "E"; - E[E["F"] = 3] = "F"; - E[E["G"] = 4] = "G"; -})(E || (E = {}));`}, - - {title: "autonumber enum #12", input: "enum E {A=-1,B}", output: `var E; -(function (E) { - E[E["A"] = -1] = "A"; - E[E["B"] = 0] = "B"; -})(E || (E = {}));`}, - - {title: "autonumber enum #13", input: "enum E {A='x',B}", output: `var E; -(function (E) { - E["A"] = "x"; - E["B"] = void 0; -})(E || (E = {}));`}, - - {title: "autonumber enum #14", input: "enum E {A,B,C=A|B,D}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 1] = "B"; - E[E["C"] = 1] = "C"; - E[E["D"] = 2] = "D"; -})(E || (E = {}));`}, - - {title: "string enum #1", input: "enum E {A = 'x',B = 'y',C = 'z'}", output: `var E; -(function (E) { - E["A"] = "x"; - E["B"] = "y"; - E["C"] = "z"; -})(E || (E = {}));`}, - - {title: "string enum #2", input: "enum E {A = 'x',B = 'y',C = `a${A}b${B}c`}", output: `var E; -(function (E) { - E["A"] = "x"; - E["B"] = "y"; - E["C"] = "axbyc"; -})(E || (E = {}));`}, - - {title: "number enum", input: "enum E {A = 0,B = 1,C = 2}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 1] = "B"; - E[E["C"] = 2] = "C"; -})(E || (E = {}));`}, - - {title: "enum self reference #1", input: "enum E {A,B=A}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 0] = "B"; -})(E || (E = {}));`}, - - {title: "enum self reference #2", input: "enum E {A=x,B=A}", output: `var E; -(function (E) { - E["A"] = x; - if (typeof E.A !== "string") E[E.A] = "A"; - E["B"] = E.A; - if (typeof E.B !== "string") E[E.B] = "B"; -})(E || (E = {}));`}, - - {title: "enum self reference #3", input: "enum E {'A'=x,B=A}", output: `var E; -(function (E) { - E["A"] = x; - if (typeof E["A"] !== "string") E[E["A"]] = "A"; - E["B"] = E.A; - if (typeof E.B !== "string") E[E.B] = "B"; -})(E || (E = {}));`}, - - {title: "enum self reference #4", input: "enum E {'A'=x,'B '=A}", output: `var E; -(function (E) { - E["A"] = x; - if (typeof E["A"] !== "string") E[E["A"]] = "A"; - E["B "] = E.A; - if (typeof E["B "] !== "string") E[E["B "]] = "B "; -})(E || (E = {}));`}, - - {title: "enum self reference #5", input: "enum E {A,B=E.A}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 0] = "B"; -})(E || (E = {}));`}, - - {title: "export enum", input: "export enum E {A, B}", output: `export { E }; -var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 1] = "B"; -})(E || (E = {}));`}, - - {title: "const enum", input: "const enum E {A, B}", output: ""}, - - {title: "merged enum", input: "enum E {A} enum E {B=A}", output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; -})(E || (E = {})); -(function (E) { - E["B"] = A; - if (typeof E.B !== "string") E[E.B] = "B"; -})(E || (E = {}));`}, - - {title: "reverse map enum", input: `enum E { - A = 0, - B = 1 << 0, - C = 1 << 1, - D, -}`, output: `var E; -(function (E) { - E[E["A"] = 0] = "A"; - E[E["B"] = 1] = "B"; - E[E["C"] = 2] = "C"; - E[E["D"] = 3] = "D"; -})(E || (E = {}));`}, - } - - for _, rec := range data { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - options := &core.CompilerOptions{} - file := parsetestutil.ParseTypeScript(rec.input, false /*jsx*/) - parsetestutil.CheckDiagnostics(t, file) - binder.BindSourceFile(file) - emitContext := printer.NewEmitContext() - resolver := binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{}) - emittestutil.CheckEmit(t, emitContext, tstransforms.NewRuntimeSyntaxTransformer(&transformers.TransformOptions{CompilerOptions: options, Context: emitContext, Resolver: resolver}).TransformSourceFile(file), rec.output) - }) - } -} - -func TestNamespaceTransformer(t *testing.T) { - t.Parallel() - data := []struct { - title string - input string - output string - }{ - {title: "empty namespace", input: "namespace N {}", output: ``}, - - {title: "export var", input: "namespace N { export var x = 1; }", output: `var N; -(function (N) { - N.x = 1; -})(N || (N = {}));`}, - - {title: "export uninitialized var", input: "namespace N { export var x; }", output: `var N; -(function (N) { -})(N || (N = {}));`}, - - {title: "exported var reference", input: "namespace N { export var x = 1; x; }", output: `var N; -(function (N) { - N.x = 1; - N.x; -})(N || (N = {}));`}, - - {title: "exported var reference across namespaces", input: "namespace N { export var x = 1; } namespace N { x; }", output: `var N; -(function (N) { - N.x = 1; -})(N || (N = {})); -(function (N) { - x; -})(N || (N = {}));`}, - - {title: "exported array binding pattern", input: "namespace N { export var [x] = [1]; }", output: `var N; -(function (N) { - [N.x] = [1]; -})(N || (N = {}));`}, - - {title: "exported array binding pattern + initializer", input: "namespace N { export var [x = 2] = [1]; }", output: `var N; -(function (N) { - [N.x = 2] = [1]; -})(N || (N = {}));`}, - - {title: "exported array binding pattern + elision", input: "namespace N { export var [, x] = [1]; }", output: `var N; -(function (N) { - [, N.x] = [1]; -})(N || (N = {}));`}, - - {title: "exported array binding pattern + rest", input: "namespace N { export var [, ...x] = [1]; }", output: `var N; -(function (N) { - [, ...N.x] = [1]; -})(N || (N = {}));`}, - - {title: "exported array binding pattern + nested array pattern", input: "namespace N { export var [[x]] = [[1]]; }", output: `var N; -(function (N) { - [[N.x]] = [[1]]; -})(N || (N = {}));`}, - - {title: "exported array binding pattern + nested object pattern", input: "namespace N { export var [{x}] = [{x: 1}]; }", output: `var N; -(function (N) { - [{ x: N.x }] = [{ x: 1 }]; -})(N || (N = {}));`}, - - {title: "exported object binding pattern", input: "namespace N { export var {x: x} = {x: 1}; }", output: `var N; -(function (N) { - ({ x: N.x } = { x: 1 }); -})(N || (N = {}));`}, - - {title: "exported object binding pattern + shorthand assignment", input: "namespace N { export var {x} = {x: 1}; }", output: `var N; -(function (N) { - ({ x: N.x } = { x: 1 }); -})(N || (N = {}));`}, - - {title: "exported object binding pattern + initializer", input: "namespace N { export var {x: x = 2} = {x: 1}; }", output: `var N; -(function (N) { - ({ x: N.x = 2 } = { x: 1 }); -})(N || (N = {}));`}, - - {title: "exported object binding pattern + shorthand assignment + initializer", input: "namespace N { export var {x = 2} = {x: 1}; }", output: `var N; -(function (N) { - ({ x: N.x = 2 } = { x: 1 }); -})(N || (N = {}));`}, - - {title: "exported object binding pattern + rest", input: "namespace N { export var {...x} = {x: 1}; }", output: `var N; -(function (N) { - ({ ...N.x } = { x: 1 }); -})(N || (N = {}));`}, - - {title: "exported object binding pattern + nested object pattern", input: "namespace N { export var {y:{x}} = {y: {x: 1}}; }", output: `var N; -(function (N) { - ({ y: { x: N.x } } = { y: { x: 1 } }); -})(N || (N = {}));`}, - - {title: "exported object binding pattern + nested array pattern", input: "namespace N { export var {y:[x]} = {y: [1]}; }", output: `var N; -(function (N) { - ({ y: [N.x] } = { y: [1] }); -})(N || (N = {}));`}, - - {title: "export function", input: "namespace N { export function f() {} }", output: `var N; -(function (N) { - function f() { } - N.f = f; -})(N || (N = {}));`}, - - {title: "export class", input: "namespace N { export class C {} }", output: `var N; -(function (N) { - class C { - } - N.C = C; -})(N || (N = {}));`}, - - {title: "export enum", input: "namespace N { export enum E {A} }", output: `var N; -(function (N) { - let E; - (function (E) { - E[E["A"] = 0] = "A"; - })(E = N.E || (N.E = {})); -})(N || (N = {}));`}, - - {title: "export namespace", input: "namespace N { export namespace N2 {} }", output: ``}, - - {title: "nested namespace", input: "namespace N.N2 { }", output: ``}, - - {title: "import=", input: "import X = Y.X;", output: `var X = Y.X;`}, - - {title: "export import= at top-level", input: "export import X = Y.X;", output: `export var X = Y.X;`}, - - {title: "export import= in namespace", input: "namespace N { export import X = Y.X; }", output: `var N; -(function (N) { - N.X = Y.X; -})(N || (N = {}));`}, - - {title: "shorthand property assignment", input: "namespace N { export var x = 1; var y = { x }; }", output: `var N; -(function (N) { - N.x = 1; - var y = { x: N.x }; -})(N || (N = {}));`}, - - {title: "shorthand property assignment pattern", input: "namespace N { export var x; ({x} = {x: 1}); }", output: `var N; -(function (N) { - ({ x: N.x } = { x: 1 }); -})(N || (N = {}));`}, - - {title: "identifier reference in template", input: `namespace N { - export var x = 1; - ` + "`" + `${x}` + "`" + ` -}`, output: `var N; -(function (N) { - N.x = 1; - ` + "`" + `${N.x}` + "`" + `; -})(N || (N = {}));`}, - } - - for _, rec := range data { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - options := &core.CompilerOptions{} - file := parsetestutil.ParseTypeScript(rec.input, false /*jsx*/) - parsetestutil.CheckDiagnostics(t, file) - binder.BindSourceFile(file) - emitContext := printer.NewEmitContext() - resolver := binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{}) - emittestutil.CheckEmit(t, emitContext, tstransforms.NewRuntimeSyntaxTransformer(&transformers.TransformOptions{CompilerOptions: options, Context: emitContext, Resolver: resolver}).TransformSourceFile(file), rec.output) - }) - } -} - -func TestParameterPropertyTransformer(t *testing.T) { - t.Parallel() - data := []struct { - title string - input string - output string - }{ - {title: "parameter properties", input: "class C { constructor(public x) { } }", output: `class C { - x; - constructor(x) { - this.x = x; - } -}`}, - {title: "parameter properties #2", input: "class C extends B { constructor(public x) { super(); } }", output: `class C extends B { - x; - constructor(x) { - super(); - this.x = x; - } -}`}, - } - - for _, rec := range data { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - options := &core.CompilerOptions{} - file := parsetestutil.ParseTypeScript(rec.input, false /*jsx*/) - parsetestutil.CheckDiagnostics(t, file) - binder.BindSourceFile(file) - emitContext := printer.NewEmitContext() - resolver := binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{}) - opts := &transformers.TransformOptions{Context: emitContext, CompilerOptions: options, Resolver: resolver} - file = tstransforms.NewTypeEraserTransformer(opts).TransformSourceFile(file) - file = tstransforms.NewRuntimeSyntaxTransformer(opts).TransformSourceFile(file) - emittestutil.CheckEmit(t, emitContext, file, rec.output) - }) - } -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/typeeraser.go b/kitcom/internal/tsgo/transformers/tstransforms/typeeraser.go deleted file mode 100644 index 0dd212a..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/typeeraser.go +++ /dev/null @@ -1,352 +0,0 @@ -package tstransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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.KindJSExportAssignment, 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()) || - !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 ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient) { - // 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 n.Body == nil { - // 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 n.Body == nil { - // 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 n.Body == nil { - // TypeScript overloads are elided - return nil - } - return tx.Factory().UpdateGetAccessorDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body)) - - case ast.KindSetAccessor: - n := node.AsSetAccessorDeclaration() - if n.Body == nil { - // TypeScript overloads are elided - return nil - } - return tx.Factory().UpdateSetAccessorDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body)) - - case ast.KindVariableDeclaration: - n := node.AsVariableDeclaration() - return tx.Factory().UpdateVariableDeclaration(n, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer)) - - case ast.KindHeritageClause: - n := node.AsHeritageClause() - if n.Token == ast.KindImplementsKeyword { - // TypeScript `implements` clauses are elided - return nil - } - return tx.Factory().UpdateHeritageClause(n, 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 n.Body == nil { - // 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) - } - 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)) - - 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)) - - 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: - n := node.AsParenthesizedExpression() - expression := ast.SkipOuterExpressions(n.Expression, ^(ast.OEKAssertions | ast.OEKExpressionsWithTypeArguments)) - 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) - } -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/typeeraser_test.go b/kitcom/internal/tsgo/transformers/tstransforms/typeeraser_test.go deleted file mode 100644 index cb452ad..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/typeeraser_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package tstransforms_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" -) - -func 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", 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(): U {} }", output: "class C {\n m() { }\n}"}, - {title: "MethodDeclaration3", input: "class C { public static m(): U {} }", output: "class C {\n static m() { }\n}"}, - {title: "GetAccessorDeclaration1", input: "class C { get m(); }", output: "class C {\n}"}, - {title: "GetAccessorDeclaration2", input: "class C { public get m(): U {} }", output: "class C {\n get m() { }\n}"}, - {title: "GetAccessorDeclaration3", input: "class C { public static get m(): U {} }", output: "class C {\n static get m() { }\n}"}, - {title: "SetAccessorDeclaration1", input: "class C { set m(v); }", output: "class C {\n}"}, - {title: "SetAccessorDeclaration2", input: "class C { public set m(v): U {} }", output: "class C {\n set m(v) { }\n}"}, - {title: "SetAccessorDeclaration3", input: "class C { public static set m(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 {}", output: "class C {\n}"}, - {title: "ClassExpression", input: "(class C {})", output: "(class C {\n});"}, - {title: "FunctionDeclaration1", input: "declare function f() {}", output: ""}, - {title: "FunctionDeclaration2", input: "function f();", output: ""}, - {title: "FunctionDeclaration3", input: "function f(): U {}", output: "function f() { }"}, - {title: "FunctionExpression", input: "(function f(): U {})", output: "(function f() { });"}, - {title: "ArrowFunction", input: "((): U => {})", output: "(() => { });"}, - {title: "ParameterDeclaration", input: "function f(this: x, a: number, b?: boolean) {}", output: "function f(a, b) { }"}, - {title: "CallExpression", input: "f()", output: "f();"}, - {title: "NewExpression1", input: "new f()", output: "new f();"}, - {title: "NewExpression2", input: "new f", output: "new f;"}, - {title: "TaggedTemplateExpression", input: "f``", output: "f ``;"}, - {title: "NonNullExpression", input: "x!", output: "x;"}, - {title: "TypeAssertionExpression#1", input: "x", output: "x;"}, - {title: "TypeAssertionExpression#2", input: "(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: " />", output: ";", jsx: true}, - {title: "JsxOpeningElement", input: ">", output: ";", 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) - }) - } -} diff --git a/kitcom/internal/tsgo/transformers/tstransforms/utilities.go b/kitcom/internal/tsgo/transformers/tstransforms/utilities.go deleted file mode 100644 index 3cb4911..0000000 --- a/kitcom/internal/tsgo/transformers/tstransforms/utilities.go +++ /dev/null @@ -1,41 +0,0 @@ -package tstransforms - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -func convertEntityNameToExpression(emitContext *printer.EmitContext, name *ast.EntityName) *ast.Expression { - if ast.IsQualifiedName(name) { - left := convertEntityNameToExpression(emitContext, name.AsQualifiedName().Left) - right := name.AsQualifiedName().Right - prop := emitContext.Factory.NewPropertyAccessExpression(left, nil /*questionDotToken*/, right, ast.NodeFlagsNone) - emitContext.SetOriginal(prop, name) - emitContext.AssignCommentAndSourceMapRanges(prop, name) - return prop - } - return name.Clone(emitContext.Factory) -} - -func constantExpression(value any, factory *printer.NodeFactory) *ast.Expression { - switch value := value.(type) { - case string: - return factory.NewStringLiteral(value) - case jsnum.Number: - if value.IsInf() || value.IsNaN() { - return nil - } - if value < 0 { - return factory.NewPrefixUnaryExpression(ast.KindMinusToken, constantExpression(-value, factory)) - } - return factory.NewNumericLiteral(value.String()) - } - return nil -} - -func isInstantiatedModule(node *ast.ModuleDeclarationNode, preserveConstEnums bool) bool { - moduleState := ast.GetModuleInstanceState(node) - return moduleState == ast.ModuleInstanceStateInstantiated || - (preserveConstEnums && moduleState == ast.ModuleInstanceStateConstEnumOnly) -} diff --git a/kitcom/internal/tsgo/transformers/utilities.go b/kitcom/internal/tsgo/transformers/utilities.go deleted file mode 100644 index c8d43f8..0000000 --- a/kitcom/internal/tsgo/transformers/utilities.go +++ /dev/null @@ -1,274 +0,0 @@ -package transformers - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/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.KindCommaListExpression, - 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.KindJSExportAssignment, - 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.KindForStatement: - return parent.AsForStatement().Initializer == name || - parent.AsForStatement().Condition == name || - parent.AsForStatement().Incrementor == name - case ast.KindForInStatement, - ast.KindForOfStatement: - return parent.AsForInOrOfStatement().Initializer == name || - parent.AsForInOrOfStatement().Expression == name - case ast.KindImportEqualsDeclaration: - return parent.AsImportEqualsDeclaration().ModuleReference == name - case ast.KindArrowFunction: - return parent.AsArrowFunction().Body == name - case ast.KindConditionalExpression: - return parent.AsConditionalExpression().Condition == name || - parent.AsConditionalExpression().WhenTrue == name || - parent.AsConditionalExpression().WhenFalse == name - case ast.KindCallExpression: - return parent.AsCallExpression().Expression == name || - slices.Contains(parent.AsCallExpression().Arguments.Nodes, name) - case ast.KindNewExpression: - return parent.AsNewExpression().Expression == name || - parent.AsNewExpression().Arguments.Nodes != nil && - slices.Contains(parent.AsNewExpression().Arguments.Nodes, name) - case ast.KindTaggedTemplateExpression: - return parent.AsTaggedTemplateExpression().Tag == name - case ast.KindImportAttribute: - return parent.AsImportAttribute().Value == name - case ast.KindJsxOpeningElement: - return parent.AsJsxOpeningElement().TagName == name - case ast.KindJsxClosingElement: - return parent.AsJsxClosingElement().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 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.GetECMALineAndCharacterOfPosition(source, original.Loc.Pos()) - endLine, _ := scanner.GetECMALineAndCharacterOfPosition(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) -}