Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate JavaScript Representation of Enums

I have a few [Flags] enums in my code I'd like to expose to JavaScript without copy&pasting. SignalR seems to be doing something similar for their Hub-proxies by mapping an URL to an Action returning the JavaScript stubs generated by reflection. Since the code is generated at runtime, it doesn't seem possible to be included into the Bundles.

As an alternative, I implemented a T4 template to generate a js file at design time:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="EnvDte" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".js" #>
Enums = {
<#
    var visualStudio = (Host as IServiceProvider).GetService(typeof(EnvDTE.DTE))
                        as EnvDTE.DTE;
    var project = visualStudio.Solution.FindProjectItem(this.Host.TemplateFile)
                                        .ContainingProject as EnvDTE.Project;

    foreach(EnvDTE.ProjectItem item in GetProjectItemsRecursively(project.ProjectItems))
    {
        if (item.FileCodeModel == null) continue;
        foreach(EnvDTE.CodeElement elem in item.FileCodeModel.CodeElements)
        {
            if (elem.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)
            {
                foreach (CodeElement innerElement in elem.Children)
                {
                    if (innerElement.Kind == vsCMElement.vsCMElementEnum)
                    {
                        CodeEnum enu = (CodeEnum)innerElement;
#>  <#= enu.Name #>: {
<#
        Dictionary<string, string> values = new Dictionary<string, string>();
        foreach (CodeElement child in enu.Members)
        {
            CodeVariable value = child as CodeVariable;

            if (value != null) {
                string init = value.InitExpression as string;
                int unused;
                if (!int.TryParse(init, out unused))
                {
                    foreach (KeyValuePair<string, string> entry in values) {
                        init = init.Replace(entry.Key, entry.Value);
                    }
                    init = "(" + init + ")"; 
                }
                values.Add(value.Name, init);
                WriteLine("\t\t" + value.Name + ": " + init + ",");
            }
        }
#>
    },
<#
                    }
                }
            }
        }
    }
#>
};
<#+
  public List<EnvDTE.ProjectItem> GetProjectItemsRecursively(EnvDTE.ProjectItems items)
  {
      var ret = new List<EnvDTE.ProjectItem>();
      if (items == null) return ret;
      foreach(EnvDTE.ProjectItem item in items)
      {
        ret.Add(item);
        ret.AddRange(GetProjectItemsRecursively(item.ProjectItems));
      }
      return ret;
  }
#>

However this feels fragile with EnvDTE. Especially the logic to handle enums like:

[Flags]
public enum Access
{
    None = 0,
    Read = 1,
    Write = 2,

    ReadWrite = Read | Write
}

with composite values is a dirty hack with string replacements. The T4 template above will generate:

Enums = {
    Access: {
        None: 0,
        Read: 1,
        Write: 2,
        ReadWrite: (1 | 2),
    },
};

Is there a cleaner way to achieve this? Ideally some kind of design-time reflection to generate the js file so it is available for bundling.

like image 984
mensi Avatar asked May 21 '13 14:05

mensi


People also ask

Can you create enums in JavaScript?

JavaScript has no support for a native enum type, but it is possible to define enums using Object s. In general, enums are a type that can hold a finite number of defined values. The values in enums are not mutable.

How do you declare an enum in JavaScript?

The easiest way to define an enum would be to use Object. freeze() in combination with a plain object. This will ensure that the enum object cannot be mutated. const daysEnum = Object.

How are enums represented in JSON?

JSON has no enum type. The two ways of modeling an enum would be: An array, as you have currently. The array values are the elements, and the element identifiers would be represented by the array indexes of the values.

How does enum work in JavaScript?

Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript. Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.


1 Answers

I think you can use Bundle Transforms to accomplish this...

http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

You can define a bundle and attach an IBundleTransform class which interpolates the content of the source files. You should be able to use reflection to write the JavaScript to the output stream.

If you wanted to do it in a hacky way this would be fairly easy to do. You could just feed it an empty file and hard-code your class to use reflection to write the JavaScript you want.

Now, if you want to design it in a way that you don't need to modify your IBundleTransform class in order to add additional enums to your javascript output, you'd need to put some extra work into a real structure. For example, let's say you have all your enums in a file called Enums.cs, you could add that file to the Include list of your bundle and you could dynamically parse it for enum declarations and then use reflection to find them by name in order to output each one in the resulting JavaScript file.

like image 53
Trevor Elliott Avatar answered Oct 26 '22 12:10

Trevor Elliott