TBR x/ref: Allow JS Enums to be used like EnumName.LabelName

MultiPart: 2/2
Change-Id: I049a64414f10e7eda31bf57e61c08c5825633006
diff --git a/lib/vdl/codegen/javascript/gen_type_def.go b/lib/vdl/codegen/javascript/gen_type_def.go
index d1a399d..76acc2a 100644
--- a/lib/vdl/codegen/javascript/gen_type_def.go
+++ b/lib/vdl/codegen/javascript/gen_type_def.go
@@ -4,6 +4,7 @@
 	"fmt"
 
 	"v.io/v23/vdl"
+	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 // makeTypeDefinitionsString generates a string that defines the specified types.
@@ -30,7 +31,11 @@
 
 	for _, def := range sortedDefs {
 		if def.Type.Name() != "" {
-			str += makeConstructorDefinitionString(def.Type, jsnames)
+			if def.Type.Kind() == vdl.Enum {
+				str += makeEnumLabelString(def.Type, jsnames)
+			} else {
+				str += makeConstructorDefinitionString(def.Type, jsnames)
+			}
 		}
 	}
 
@@ -112,6 +117,24 @@
 	return fmt.Sprintf("module.exports.%s = %s;\n", name, ctorName)
 }
 
+// makeEnumLabelString creates a string that defines the labels in an enum.
+// e.g. `module.Exports.MyEnum = {
+//   ALabel: (Registry.lookupOrCreateConstructor(_typeMyEnum))("ALabel"),
+//   BLabel: (Registry.lookupOrCreateConstructor(_typeMyEnum))("BLabel"),
+// }`
+
+func makeEnumLabelString(t *vdl.Type, jsnames typeNames) string {
+	_, name := vdl.SplitIdent(t.Name())
+	str := fmt.Sprintf("module.exports.%s = {\n", name)
+	for i := 0; i < t.NumEnumLabel(); i++ {
+		enumVal := vdl.ZeroValue(t)
+		enumVal.AssignEnumIndex(i)
+		str += fmt.Sprintf("  %s: %s,\n", vdlutil.ToConstCase(t.EnumLabel(i)), typedConst(jsnames, enumVal))
+	}
+	str += "};\n"
+	return str
+}
+
 func jsKind(k vdl.Kind) string {
 	switch k {
 	case vdl.Any:
diff --git a/lib/vdl/codegen/javascript/pkg_types.go b/lib/vdl/codegen/javascript/pkg_types.go
index f7445d9..7f986da 100644
--- a/lib/vdl/codegen/javascript/pkg_types.go
+++ b/lib/vdl/codegen/javascript/pkg_types.go
@@ -6,6 +6,7 @@
 	"strings"
 
 	"v.io/x/ref/lib/vdl/compile"
+	"v.io/x/ref/lib/vdl/vdlutil"
 
 	"v.io/v23/vdl"
 )
@@ -52,6 +53,10 @@
 		return name
 	}
 
+	if t.Kind() == vdl.Enum {
+		return fmt.Sprintf("%s.%s._type", tn.LookupConstructor(t), vdlutil.ToConstCase(t.EnumLabel(0)))
+	}
+
 	return "new " + tn.LookupConstructor(t) + "()._type"
 }
 
diff --git a/lib/vdl/codegen/javascript/type_test.go b/lib/vdl/codegen/javascript/type_test.go
index bc3df73..7e932f2 100644
--- a/lib/vdl/codegen/javascript/type_test.go
+++ b/lib/vdl/codegen/javascript/type_test.go
@@ -57,6 +57,9 @@
 					{
 						Type: vdl.ListType(vdl.ByteType),
 					},
+					{
+						Type: vdl.NamedType("ColorsBeginningWithAOrB", vdl.EnumType("Aqua", "Beige")),
+					},
 				},
 			},
 		},
@@ -75,6 +78,7 @@
 
 	expectedResult := `var _type1 = new vdl.Type();
 var _type2 = new vdl.Type();
+var _typeColorsBeginningWithAOrB = new vdl.Type();
 var _typeNamedList = new vdl.Type();
 var _typeNamedStruct = new vdl.Type();
 _type1.kind = vdl.Kind.LIST;
@@ -83,6 +87,9 @@
 _type2.kind = vdl.Kind.LIST;
 _type2.name = "";
 _type2.elem = vdl.Types.BYTE;
+_typeColorsBeginningWithAOrB.kind = vdl.Kind.ENUM;
+_typeColorsBeginningWithAOrB.name = "ColorsBeginningWithAOrB";
+_typeColorsBeginningWithAOrB.labels = ["Aqua", "Beige"];
 _typeNamedList.kind = vdl.Kind.LIST;
 _typeNamedList.name = "NamedList";
 _typeNamedList.elem = _typeNamedStruct;
@@ -91,8 +98,13 @@
 _typeNamedStruct.fields = [{name: "List", type: _typeNamedList}, {name: "Bool", type: new otherPkg.NamedBool()._type}, {name: "UnnamedTypeField", type: _type1}];
 _type1.freeze();
 _type2.freeze();
+_typeColorsBeginningWithAOrB.freeze();
 _typeNamedList.freeze();
 _typeNamedStruct.freeze();
+module.exports.ColorsBeginningWithAOrB = {
+  AQUA: canonicalize.reduce(new (vdl.Registry.lookupOrCreateConstructor(_typeColorsBeginningWithAOrB))('Aqua', true), _typeColorsBeginningWithAOrB),
+  BEIGE: canonicalize.reduce(new (vdl.Registry.lookupOrCreateConstructor(_typeColorsBeginningWithAOrB))('Beige', true), _typeColorsBeginningWithAOrB),
+};
 module.exports.NamedList = (vdl.Registry.lookupOrCreateConstructor(_typeNamedList));
 module.exports.NamedStruct = (vdl.Registry.lookupOrCreateConstructor(_typeNamedStruct));
 `