package printer_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/printer" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" "gotest.tools/v3/assert" ) func TestTempVariable1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewTempVariable() name2 := ec.Factory.NewTempVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "_a", text1) assert.Equal(t, "_b", text2) } func TestTempVariable2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewTempVariableEx(printer.AutoGenerateOptions{ Prefix: "A", Suffix: "B", }) name2 := ec.Factory.NewTempVariableEx(printer.AutoGenerateOptions{ Prefix: "A", Suffix: "B", }) g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "A_aB", text1) assert.Equal(t, "A_bB", text2) } func TestTempVariable3(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewTempVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name1) assert.Equal(t, "_a", text1) assert.Equal(t, "_a", text2) } func TestTempVariableScoped(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewTempVariable() name2 := ec.Factory.NewTempVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) g.PushScope(false) text2 := g.GenerateName(name2) g.PopScope(false) assert.Equal(t, "_a", text1) assert.Equal(t, "_a", text2) } func TestTempVariableScopedReserved(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewTempVariableEx(printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}) name2 := ec.Factory.NewTempVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) g.PushScope(false) text2 := g.GenerateName(name2) g.PopScope(false) assert.Equal(t, "_a", text1) assert.Equal(t, "_b", text2) } func TestLoopVariable1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewLoopVariable() name2 := ec.Factory.NewLoopVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "_i", text1) assert.Equal(t, "_a", text2) } func TestLoopVariable2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewLoopVariableEx(printer.AutoGenerateOptions{ Prefix: "A", Suffix: "B", }) name2 := ec.Factory.NewLoopVariableEx(printer.AutoGenerateOptions{ Prefix: "A", Suffix: "B", }) g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "A_iB", text1) assert.Equal(t, "A_aB", text2) } func TestLoopVariable3(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewLoopVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name1) assert.Equal(t, "_i", text1) assert.Equal(t, "_i", text2) } func TestLoopVariableScoped(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewLoopVariable() name2 := ec.Factory.NewLoopVariable() g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) g.PushScope(false) text2 := g.GenerateName(name2) g.PopScope(false) assert.Equal(t, "_i", text1) assert.Equal(t, "_i", text2) } func TestUniqueName1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewUniqueName("foo") name2 := ec.Factory.NewUniqueName("foo") g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "foo_1", text1) assert.Equal(t, "foo_2", text2) } func TestUniqueName2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewUniqueName("foo") g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name1) assert.Equal(t, "foo_1", text1) // Expected to be same because GenerateName goes off object identity assert.Equal(t, "foo_1", text2) } func TestUniqueNameScoped(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewUniqueName("foo") name2 := ec.Factory.NewUniqueName("foo") g := &printer.NameGenerator{Context: ec} assert.Equal(t, "foo_1", g.GenerateName(name1)) g.PushScope(false) assert.Equal(t, "foo_2", g.GenerateName(name2)) // Matches Strada, but is incorrect // assert.Equal(t, "foo_1", g.GenerateName(name2)) // TODO: Fix after Strada port is complete. g.PopScope(false) } func TestUniquePrivateName1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewUniquePrivateName("#foo") name2 := ec.Factory.NewUniquePrivateName("#foo") g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "#foo_1", text1) assert.Equal(t, "#foo_2", text2) } func TestUniquePrivateName2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewUniquePrivateName("#foo") g := &printer.NameGenerator{Context: ec} text1 := g.GenerateName(name1) text2 := g.GenerateName(name1) assert.Equal(t, "#foo_1", text1) assert.Equal(t, "#foo_1", text2) } func TestUniquePrivateNameScoped(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() name1 := ec.Factory.NewUniquePrivateName("#foo") name2 := ec.Factory.NewUniquePrivateName("#foo") g := &printer.NameGenerator{Context: ec} assert.Equal(t, "#foo_1", g.GenerateName(name1)) g.PushScope(false) // private names are always reserved in nested scopes assert.Equal(t, "#foo_2", g.GenerateName(name2)) g.PopScope(false) } func TestGeneratedNameForIdentifier1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("function f() {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].Name() name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "f_1", text1) } func TestGeneratedNameForIdentifier2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("function f() {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].Name() name1 := ec.Factory.NewGeneratedNameForNodeEx(n, printer.AutoGenerateOptions{ Prefix: "a", Suffix: "b", }) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "afb", text1) } func TestGeneratedNameForIdentifier3(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("function f() {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].Name() name1 := ec.Factory.NewGeneratedNameForNodeEx(n, printer.AutoGenerateOptions{ Prefix: "a", Suffix: "b", }) name2 := ec.Factory.NewGeneratedNameForNode(name1) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name2) assert.Equal(t, "afb_1", text1) } // namespace reuses name if it does not collide with locals func TestGeneratedNameForNamespace1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("namespace foo { }", false /*jsx*/) binder.BindSourceFile(file) ns1 := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(ns1) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "foo", text1) } // namespace uses generated name if it collides with locals func TestGeneratedNameForNamespace2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("namespace foo { var foo; }", false /*jsx*/) binder.BindSourceFile(file) ns1 := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(ns1) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "foo_1", text1) } // avoids collisions when unscoped func TestGeneratedNameForNamespace3(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("namespace ns1 { namespace foo { var foo; } } namespace ns2 { namespace foo { var foo; } }", false /*jsx*/) binder.BindSourceFile(file) ns1 := file.Statements.Nodes[0].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] ns2 := file.Statements.Nodes[1].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(ns1) name2 := ec.Factory.NewGeneratedNameForNode(ns2) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "foo_1", text1) assert.Equal(t, "foo_2", text2) } // reuse name when scoped func TestGeneratedNameForNamespace4(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("namespace ns1 { namespace foo { var foo; } } namespace ns2 { namespace foo { var foo; } }", false /*jsx*/) binder.BindSourceFile(file) ns1 := file.Statements.Nodes[0].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] ns2 := file.Statements.Nodes[1].AsModuleDeclaration().Body.AsModuleBlock().Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(ns1) name2 := ec.Factory.NewGeneratedNameForNode(ns2) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} g.PushScope(false) text1 := g.GenerateName(name1) g.PopScope(false) g.PushScope(false) text2 := g.GenerateName(name2) g.PopScope(false) assert.Equal(t, "foo_1", text1) assert.Equal(t, "foo_2", text2) // Matches Strada, but is incorrect // assert.Equal(t, "foo_1", text2) // TODO: Fix after Strada port is complete. } func TestGeneratedNameForNodeCached(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("namespace foo { var foo; }", false /*jsx*/) binder.BindSourceFile(file) ns1 := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(ns1) name2 := ec.Factory.NewGeneratedNameForNode(ns1) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) text2 := g.GenerateName(name2) assert.Equal(t, "foo_1", text1) assert.Equal(t, "foo_1", text2) } func TestGeneratedNameForImport(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("import * as foo from 'foo'", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "foo_1", text1) } func TestGeneratedNameForExport(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("export * as foo from 'foo'", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "foo_1", text1) } func TestGeneratedNameForFunctionDeclaration1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("export function f() {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "f_1", text1) } func TestGeneratedNameForFunctionDeclaration2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("export default function () {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "default_1", text1) } func TestGeneratedNameForClassDeclaration1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("export class C {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "C_1", text1) } func TestGeneratedNameForClassDeclaration2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("export default class {}", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "default_1", text1) } func TestGeneratedNameForExportAssignment(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("export default 0", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "default_1", text1) } func TestGeneratedNameForClassExpression(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("(class {})", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].AsExpressionStatement().Expression.AsParenthesizedExpression().Expression name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "class_1", text1) } func TestGeneratedNameForMethod1(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("class C { m() {} }", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "m_1", text1) } func TestGeneratedNameForMethod2(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("class C { 0() {} }", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0] name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "_a", text1) } func TestGeneratedPrivateNameForMethod(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("class C { m() {} }", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0] name1 := ec.Factory.NewGeneratedPrivateNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "#m_1", text1) } func TestGeneratedNameForComputedPropertyName(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("class C { [x] }", false /*jsx*/) binder.BindSourceFile(file) n := file.Statements.Nodes[0].AsClassDeclaration().Members.Nodes[0].Name() name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "_a", text1) } func TestGeneratedNameForOther(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := parsetestutil.ParseTypeScript("class C { [x] }", false /*jsx*/) binder.BindSourceFile(file) n := ec.Factory.NewObjectLiteralExpression( ec.Factory.NewNodeList([]*ast.Node{}), false, /*multiLine*/ ) name1 := ec.Factory.NewGeneratedNameForNode(n) g := &printer.NameGenerator{Context: ec, GetTextOfNode: (*ast.Node).Text} text1 := g.GenerateName(name1) assert.Equal(t, "_a", text1) }