remove unused packages

This commit is contained in:
Egor Aristov 2025-10-15 17:30:13 +03:00
parent f1795ab4d0
commit 79197d8157
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
41 changed files with 0 additions and 14624 deletions

View File

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

View File

@ -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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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<HasInferredType>(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<never>(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<PrimitiveLiteral>(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<never>(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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<AnonymousFunctionDefinition>*/) *ast.StringLiteralNode {
original := emitContext.MostOriginal(ast.SkipOuterExpressions(expression, ast.OEKAll))
if (ast.IsClassDeclaration(original) || ast.IsFunctionDeclaration(original)) &&
original.Name() == nil && ast.HasSyntacticModifier(original, ast.ModifierFlagsDefault) {
return emitContext.Factory.NewStringLiteral("default")
}
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<AnonymousFunctionDefinition>,
assignedName *ast.Expression,
ignoreEmptyStringLiteral bool,
) *ast.Expression {
if ignoreEmptyStringLiteral && ast.IsStringLiteral(assignedName) && len(assignedName.Text()) == 0 {
return expression
}
factory := emitContext.Factory
innerExpression := ast.SkipOuterExpressions(expression, ast.OEKAll)
var updatedExpression *ast.Expression
if ast.IsClassExpression(innerExpression) {
updatedExpression = injectClassNamedEvaluationHelperBlockIfMissing(emitContext, innerExpression, assignedName, nil /*thisExpression*/)
} else {
updatedExpression = factory.NewSetFunctionNameHelper(innerExpression, assignedName, "" /*prefix*/)
}
return factory.RestoreOuterExpressions(expression, updatedExpression, ast.OEKAll)
}
func transformNamedEvaluationOfPropertyAssignment(context *printer.EmitContext, node *ast.PropertyAssignment /*NamedEvaluation & PropertyAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.2.5.5 RS: PropertyDefinitionEvaluation
// PropertyAssignment : PropertyName `:` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and _isProtoSetter_ is *false*, then
// a. Let _popValue_ be ? NamedEvaluation of |AssignmentExpression| with argument _propKey_.
// ...
factory := context.Factory
assignedName, name := getAssignedNameOfPropertyName(context, node.Name(), assignedNameText)
initializer := finishTransformNamedEvaluation(context, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdatePropertyAssignment(node, nil /*modifiers*/, name, nil /*postfixToken*/, nil /*typeNode*/, initializer)
}
func transformNamedEvaluationOfShorthandAssignmentProperty(emitContext *printer.EmitContext, node *ast.ShorthandPropertyAssignment /*NamedEvaluation & ShorthandPropertyAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.15.5.3 RS: PropertyDestructuringAssignmentEvaluation
// AssignmentProperty : IdentifierReference Initializer?
// ...
// 4. If |Initializer?| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _P_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText)
} 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")
}
}

View File

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

View File

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

View File

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

View File

@ -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 {
// <bodyStatements>
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
// when `async` is `true`, produces:
//
// try {
// <bodyStatements>
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// const result_1 = __disposeResources(env_1);
// if (result_1) {
// await result_1;
// }
// }
// Unfortunately, it is necessary to use two properties to indicate an error because `throw undefined` is legal
// JavaScript.
tryBlock := tx.Factory().NewBlock(tx.Factory().NewNodeList(bodyStatements), true /*multiLine*/)
bodyCatchBinding := tx.Factory().NewUniqueName("e")
catchClause := tx.Factory().NewCatchClause(
tx.Factory().NewVariableDeclaration(
bodyCatchBinding,
nil, /*exclamationToken*/
nil, /*type*/
nil, /*initializer*/
),
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("error"), ast.NodeFlagsNone),
bodyCatchBinding,
),
),
tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("hasError"), ast.NodeFlagsNone),
tx.Factory().NewTrueExpression(),
),
),
}), true /*multiLine*/),
)
var finallyBlock *ast.BlockNode
if async {
result := tx.Factory().NewUniqueName("result")
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(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
}

View File

@ -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(),
),
)
}

View File

@ -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, "*/", "*_/")
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 /// <amd-module name="<new name>" />
// 2. --out or --outFile is used, making the name relative to the rootDir
// 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System).
//
// Otherwise, a new StringLiteral node representing the module name will be returned.
func getExternalModuleNameLiteral(factory *printer.NodeFactory, importNode *ast.Node /*ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall*/, sourceFile *ast.SourceFile, host any /*EmitHost*/, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions) *ast.StringLiteralNode {
moduleName := ast.GetExternalModuleName(importNode)
if moduleName != nil && ast.IsStringLiteral(moduleName) {
name := tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions)
if name == nil {
name = tryRenameExternalModule(factory, moduleName, sourceFile)
}
if name == nil {
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 /// <amd-module name="<new name>" />
// 2. --out or --outFile is used, making the name relative to the rootDir
//
// Otherwise, a new StringLiteral node representing the module name will be returned.
func tryGetModuleNameFromFile(factory *printer.NodeFactory, file *ast.SourceFile, host any /*EmitHost*/, options *core.CompilerOptions) *ast.StringLiteralNode {
if file == nil {
return nil
}
// !!!
// if file.moduleName {
// return factory.createStringLiteral(file.moduleName)
// }
return nil
}
func tryGetModuleNameFromDeclaration(declaration *ast.Node /*ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall*/, host any /*EmitHost*/, factory *printer.NodeFactory, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions) *ast.StringLiteralNode {
if resolver == nil {
return nil
}
return tryGetModuleNameFromFile(factory, resolver.GetExternalModuleFileFromDeclaration(declaration), host, compilerOptions)
}
// Resolves a local path to a path which is absolute to the base of the emit
func getExternalModuleNameFromPath(host any /*ResolveModuleNameResolutionHost*/, fileName string, referencePath string) string {
// !!!
return ""
}
// Some bundlers (SystemJS builder) sometimes want to rename dependencies.
// Here we check if alternative name was provided for a given moduleName and return it if possible.
func tryRenameExternalModule(factory *printer.NodeFactory, moduleName *ast.LiteralExpression, sourceFile *ast.SourceFile) *ast.StringLiteralNode {
// !!!
return nil
}
func isFileLevelReservedGeneratedIdentifier(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
info := emitContext.GetAutoGenerateInfo(name)
return info != nil &&
info.Flags.IsFileLevel() &&
info.Flags.IsOptimistic() &&
info.Flags.IsReservedInNestedScopes()
}
// A simple inlinable expression is an expression which can be copied into multiple locations
// without risk of repeating any sideeffects and whose value could not possibly change between
// any such locations
func isSimpleInlineableExpression(expression *ast.Expression) bool {
return !ast.IsIdentifier(expression) && transformers.IsSimpleCopiableExpression(expression)
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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