diff --git a/build/T4TS.cs b/build/T4TS.cs
index 0a219a1..85cf00e 100644
--- a/build/T4TS.cs
+++ b/build/T4TS.cs
@@ -1,14 +1,53 @@
-namespace T4TS
+
+namespace T4TS
{
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
- [GeneratedCode("T4TS", "0.1")]
- public class TypeScriptInterfaceAttribute : Attribute
+ ///
+ /// Add this attribute to a class to generate a corresponding TypeScript interface.
+ ///
+ [GeneratedCode("T4TS", "1.0")]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
+ public class TypeScriptInterfaceAttribute: Attribute
{
- public TypeScriptInterfaceAttribute()
- {
- }
+ ///
+ /// Specifies which module the interface should be placed.
+ /// The default module will be used if not specified.
+ ///
+ public string Module { get; set; }
+
+ ///
+ /// The name of the interface.
+ /// If not specified, the name of the class will be used.
+ ///
+ public string Name { get; set; }
+ }
+
+ ///
+ /// Add this attribute to a property to customize the generated interface member
+ ///
+ [GeneratedCode("T4TS", "1.0")]
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=true)]
+ public class TypeScriptMemberAttribute: Attribute
+ {
+ ///
+ /// The member name in the interface.
+ /// If not specified, the property name will be used.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Specify if the member should be optional, ie. "name?: type".
+ /// If not specified, the default value will be used.
+ ///
+ public bool Optional { get; set; }
+
+ ///
+ /// Specify which type the interface member will have.
+ /// If not specified, a suitable type will be determined.
+ ///
+ public string Type { get; set; }
}
}
diff --git a/build/T4TS.tt b/build/T4TS.tt
index 143817b..e32a61a 100644
--- a/build/T4TS.tt
+++ b/build/T4TS.tt
@@ -1,90 +1,122 @@
<#@ template language="C#" debug="true" hostspecific="true"
#><#@ output extension=".cs"
#><#@ assembly name="System.Core"
-#><#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0"
-#><#@ assembly name="EnvDTE"
-#><#@ assembly name="EnvDTE80"
-#><#@ assembly name="VSLangProj"
-#><#@ assembly name="System.Xml"
-#><#@ assembly name="System.Xml.Linq"
+#><#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0"
+#><#@ assembly name="EnvDTE"
+#><#@ assembly name="EnvDTE80"
#><#@ import namespace="System.Collections.Generic"
#><#@ import namespace="System.IO"
#><#@ import namespace="System.Linq"
#><#@ import namespace="System.Text"
-#><#@ import namespace="System.Text.RegularExpressions"
#><#@ import namespace="Microsoft.VisualStudio.Shell.Interop"
-#><#@ import namespace="EnvDTE"
-#><#@ import namespace="EnvDTE80"
#><#@ import namespace="Microsoft.VisualStudio.TextTemplating"
-#><# PrepareDataToRender(this); var manager = Manager.Create(Host, GenerationEnvironment); manager.StartNewFile("T4TS.d.ts"); #>
-/************************************************
- Generated by T4TS
- Changes made in this file will be overwritten.
-************************************************/
-
-module Api {
-
-<# foreach(var tsInterface in output) { #>
+#><#@ import namespace="EnvDTE"
+#><#@ Include File="T4TS.tt.settings.t4"
+#><# var manager = Manager.Create(Host, GenerationEnvironment); manager.StartNewFile("T4TS.d.ts"); #>
+/****************************************************************************
+ Generated by T4TS.tt - don't make any changes in this file
+****************************************************************************/
+<# foreach(var module in GetDataToRender()) { #>
+
+<#= module.IsGlobal ? "// -- Begin global interfaces" : "module "+module.QualifiedName + " {" #>
+<# foreach(var tsInterface in module.Interfaces) { #>
/** Generated from <#= tsInterface.FullName #> **/
- export interface <#= tsInterface.Name #> {
+ <#= module.IsGlobal?"":"export "#>interface <#= tsInterface.Name #> {
<# foreach(var member in tsInterface.Members) { #>
- <#= member.Name #>: <#=member.Type #>;
+ <#= member.Name + (member.Optional ? "?" : "") #>: <#=member.Type #>;
<# } #>
-<# if (tsInterface.IndexerType != null) { #>
- [index: number]: <#= tsInterface.IndexerType #>;
+<# if (tsInterface.IndexedType != null) { #>
+ [index: number]: <#= tsInterface.IndexedType #>;
<# } #>
}
-
<# } #>
-}
-<#manager.EndBlock();
-#>
+<#= module.IsGlobal ? "// -- End global interfaces" : "}" #>
+<# } #>
+<# manager.EndBlock(); #>
+
namespace T4TS
{
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
- [GeneratedCode("T4TS", "0.1")]
- public class TypeScriptInterfaceAttribute : Attribute
+ ///
+ /// Add this attribute to a class to generate a corresponding TypeScript interface.
+ ///
+ [GeneratedCode("T4TS", "1.0")]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
+ public class TypeScriptInterfaceAttribute: Attribute
{
- public TypeScriptInterfaceAttribute()
- {
- }
+ ///
+ /// Specifies which module the interface should be placed.
+ /// The default module will be used if not specified.
+ ///
+ public string Module { get; set; }
+
+ ///
+ /// The name of the interface.
+ /// If not specified, the name of the class will be used.
+ ///
+ public string Name { get; set; }
+ }
+
+ ///
+ /// Add this attribute to a property to customize the generated interface member
+ ///
+ [GeneratedCode("T4TS", "1.0")]
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=true)]
+ public class TypeScriptMemberAttribute: Attribute
+ {
+ ///
+ /// The member name in the interface.
+ /// If not specified, the property name will be used.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Specify if the member should be optional, ie. "name?: type".
+ /// If not specified, the default value will be used.
+ ///
+ public bool Optional { get; set; }
+
+ ///
+ /// Specify which type the interface member will have.
+ /// If not specified, a suitable type will be determined.
+ ///
+ public string Type { get; set; }
}
}
-<#manager.Process(true); #><#+
-
-static DTE Dte;
-static Project Project;
-static TextTransformation TT;
-static string attributeFullName = "T4TS.TypeScriptInterfaceAttribute";
-static List output;
+<#
+manager.Process(true);
+#><#+
-void PrepareDataToRender(TextTransformation tt) {
- TT = tt;
+List GetDataToRender() {
+ DTE dte = null;
// Get the DTE service from the host
var serviceProvider = Host as IServiceProvider;
- if (serviceProvider != null) {
- Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
- }
+ if (serviceProvider != null)
+ dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
// Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
- if (Dte == null) {
+ if (dte == null)
throw new Exception("Can only execute through the Visual Studio host");
- }
- Project = GetProjectContainingT4File(Dte);
-
- if (Project == null) {
- Error("Could not find the VS Project containing the T4TS file.");
- return;
- }
+ var project = GetProjectContainingT4File(dte);
+
+ if (project == null)
+ throw new Exception("Could not find the VS project containing the T4TS file.");
+
+ // Read settings from T4TS.tt.settings.tt
+ var settings = new Settings
+ {
+ DefaultModule = DefaultModule,
+ DefaultOptional = DefaultOptional
+ };
- var generator = new CodeGenerator(Project);
+ var generator = new CodeGenerator(project, settings);
- output = generator.GetInterfaces().ToList();
+ return generator.GetAllInterfaces().ToList();
}
Project GetProjectContainingT4File(DTE dte) {
@@ -104,148 +136,252 @@ Project GetProjectContainingT4File(DTE dte) {
return projectItem.ContainingProject;
}
-public class CodeGenerator
+// -- Models ----------------------------------------------------------------------------------
+
+class Settings
{
- public Project Project { get; private set; }
- private static readonly string AttributeFullName = "T4TS.TypeScriptInterfaceAttribute";
+ ///
+ /// The default module of the generated interface, if not specified by the TypeScriptInterfaceAttribute
+ ///
+ public string DefaultModule { get; set; }
+
+ ///
+ /// The default value for Optional, if not specified by the TypeScriptMemberAttribute
+ ///
+ public bool DefaultOptional { get; set; }
+}
+class TypeScriptInterface
+{
+ public string Name { get; set; }
+ public string FullName { get; set; }
- private static readonly string[] genericCollectionTypeStarts = new string[] {
- "System.Collections.Generic.List<",
- "System.Collections.Generic.IList<",
- "System.Collections.Generic.ICollection<"
- };
+ public List Members { get; set; }
+ public TypescriptType IndexedType { get; set; }
- public CodeGenerator(Project project)
+ public TypeScriptInterface()
+ {
+ Members = new List();
+ }
+}
+class TypeScriptInterfaceAttributeValues
+{
+ public string Module { get; set; }
+ public string Name { get; set; }
+}
+class TypeScriptInterfaceMember
+{
+ public string Name { get; set; }
+ public TypescriptType Type { get; set; }
+ public bool Optional { get; set; }
+ public string FullName { get; set; }
+}
+class TypeScriptMemberAttributeValues
+{
+ public string Name { get; set; }
+ public bool Optional { get; set; }
+ public string Type { get; set; }
+}
+class TypeScriptModule
+{
+ public string QualifiedName { get; set; }
+ public List Interfaces { get; set; }
+ public bool IsGlobal
{
+ get { return string.IsNullOrWhiteSpace(QualifiedName); }
+ }
- this.Project = project;
+ public TypeScriptModule()
+ {
+ Interfaces = new List();
+ }
+}
+
+// -- Traversal ----------------------------------------------------------------------------------
+
+class ClassTraverser
+{
+ public CodeClass CodeClass { get; private set; }
+ public Action WithProperty { get; set; }
+
+ public ClassTraverser(CodeClass codeClass, Action withProperty)
+ {
+ if (codeClass == null)
+ throw new ArgumentNullException("codeClass");
+
+ if (withProperty == null)
+ throw new ArgumentNullException("withProperty");
+
+ this.CodeClass = codeClass;
+ this.WithProperty = withProperty;
+
+ if (codeClass.Members != null)
+ Traverse(codeClass.Members);
+ }
+
+ private void Traverse(CodeElements members)
+ {
+ foreach (var property in members.OfType())
+ WithProperty(property);
}
+}
+class NamespaceTraverser
+{
+ public Action WithCodeClass { get; private set; }
+
+ public NamespaceTraverser(CodeNamespace ns, Action withCodeClass)
+ {
+ if (ns == null)
+ throw new ArgumentNullException("ns");
+
+ if (withCodeClass == null)
+ throw new ArgumentNullException("withCodeClass");
+
+ WithCodeClass = withCodeClass;
+
+ if (ns.Members != null)
+ Traverse(ns.Members);
+ }
+
+ private void Traverse(CodeElements members)
+ {
+ foreach (var codeClass in members.OfType())
+ WithCodeClass(codeClass);
+ }
+}
+class ProjectTraverser
+{
+ public Action WithNamespace { get; private set; }
- public IEnumerable GetInterfaces()
+ public ProjectTraverser(Project project, Action withNamespace)
{
- // A context that holds all interfaces that will be generated.
- // Keyed on the FullName of the CodeType.
- var typeContext = new TypeContext(VisitProjectItems(Project.ProjectItems)
- .ToDictionary(i => i.CodeType.FullName, i => i));
+ if (project == null)
+ throw new ArgumentNullException("project");
+
+ if (withNamespace == null)
+ throw new ArgumentNullException("withNamespace");
- foreach (var instance in typeContext.Values)
- yield return GetInterface(instance, typeContext);
+ WithNamespace = withNamespace;
+
+ if (project.ProjectItems != null)
+ Traverse(project.ProjectItems);
}
- private IEnumerable VisitProjectItems(ProjectItems items)
+ private void Traverse(ProjectItems items)
{
foreach (ProjectItem pi in items)
{
if (pi.FileCodeModel != null)
{
var codeElements = pi.FileCodeModel.CodeElements;
- foreach (CodeElement codeElement in codeElements)
- {
- if (codeElement is CodeNamespace)
- {
- foreach (var instance in FindClassesWithAttribute(codeElement as CodeNamespace))
- yield return instance;
- }
- }
+ foreach (var ns in codeElements.OfType())
+ WithNamespace(ns);
}
- if (pi.ProjectItems != null && pi.ProjectItems.Count > 0)
- {
- foreach (var tsInterface in VisitProjectItems(pi.ProjectItems))
- yield return tsInterface;
- }
+ if (pi.ProjectItems != null)
+ Traverse(pi.ProjectItems);
}
}
+}
- private IEnumerable FindClassesWithAttribute(CodeNamespace ns)
- {
- foreach (CodeElement codeElement in ns.Members)
- {
- if (codeElement is CodeType)
- {
- var ct = codeElement as CodeType;
- if (ct.Attributes == null)
- continue;
+// -- Types ----------------------------------------------------------------------------------
- foreach (CodeAttribute attr in ct.Attributes)
- {
- if (attr.FullName == AttributeFullName)
- {
- yield return new AttributeDecoratedInstance
- {
- CodeType = ct,
- Namespace = ns,
- TypescriptType = ct.Name
- };
-
- break;
- }
- }
- }
- }
+class TypescriptType
+{
+ public virtual string Name { get { return "any"; } }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+}
+class StringType: TypescriptType
+{
+ public override string Name
+ {
+ get { return "string"; }
+ }
+}
+class NumberType : TypescriptType
+{
+ public override string Name
+ {
+ get { return "number"; }
+ }
+}
+class BoolType: TypescriptType
+{
+ public override string Name
+ {
+ get { return "bool"; }
}
+}
+class ArrayType: TypescriptType
+{
+ public TypescriptType ElementType { get; set; }
- private TypeScriptInterface GetInterface(AttributeDecoratedInstance instance, TypeContext typeContext)
+ public override string ToString()
{
- var tsInterface = new TypeScriptInterface
- {
- FullName = instance.CodeType.FullName,
- Name = instance.CodeType.Name,
- Members = GetMembers(instance, typeContext).ToList()
- };
+ return ElementType.ToString() + "[]";
+ }
+}
+class CustomType: TypescriptType
+{
+ private string m_name;
- if (instance.CodeType.Bases.Count > 0)
- {
- foreach (CodeElement elem in instance.CodeType.Bases)
- {
- if (genericCollectionTypeStarts.Any(elem.FullName.StartsWith))
- {
- string fullName = UnwrapGenericType(elem.FullName);
- if (typeContext.ContainsKey(fullName))
- {
- tsInterface.IndexerType = typeContext[fullName].CodeType.Name;
- return tsInterface;
- }
- }
- }
- }
+ public override string Name
+ {
+ get { return m_name; }
+ }
+
+ public string QualifedModule { get; private set; }
- return tsInterface;
+ public CustomType(string name, string qualifiedModule=null)
+ {
+ m_name = name;
+ this.QualifedModule = qualifiedModule;
}
- private IEnumerable GetMembers(AttributeDecoratedInstance instance, TypeContext typeContext)
+ public override string ToString()
{
- foreach (CodeElement codeElement in instance.CodeType.Members)
- {
- if (!(codeElement is CodeProperty))
- continue;
+ if (string.IsNullOrWhiteSpace(QualifedModule))
+ return base.ToString();
- var codeProperty = (CodeProperty)codeElement;
- if (codeProperty.Access != vsCMAccess.vsCMAccessPublic)
- continue;
+ return QualifedModule + "." + base.ToString();
+ }
+}
+class TypeContext
+{
+ private static readonly string[] genericCollectionTypeStarts = new string[] {
+ "System.Collections.Generic.List<",
+ "System.Collections.Generic.IList<",
+ "System.Collections.Generic.ICollection<"
+ };
- var func = codeProperty.Getter;
- if (func != null)
- {
- yield return new TypeScriptInterfaceMember
- {
- Name = codeProperty.Name,
- Type = GetTypeScriptType(instance, func.Type, codeProperty, typeContext)
- };
- }
- }
+ ///
+ /// Lookup table for "custom types", ie. non-builtin types. Keyed on the FullName of the type.
+ ///
+ private Dictionary customTypes = new Dictionary();
+
+ public void AddCustomType(string typeFullName, CustomType customType)
+ {
+ customTypes.Add(typeFullName, customType);
+ }
+
+ public bool TryGetCustomType(string typeFullName, out CustomType customType)
+ {
+ return customTypes.TryGetValue(typeFullName, out customType);
}
- private string GetTypeScriptType(AttributeDecoratedInstance instance, CodeTypeRef codeType, CodeProperty codeProperty, TypeContext typeContext)
+ public TypescriptType GetTypeScriptType(CodeTypeRef codeType)
{
switch (codeType.TypeKind)
{
case vsCMTypeRef.vsCMTypeRefChar:
case vsCMTypeRef.vsCMTypeRefString:
- return "string";
+ return new StringType();
case vsCMTypeRef.vsCMTypeRefBool:
- return "bool";
+ return new BoolType();
case vsCMTypeRef.vsCMTypeRefByte:
case vsCMTypeRef.vsCMTypeRefDouble:
@@ -254,40 +390,47 @@ public class CodeGenerator
case vsCMTypeRef.vsCMTypeRefFloat:
case vsCMTypeRef.vsCMTypeRefLong:
case vsCMTypeRef.vsCMTypeRefDecimal:
- return "number";
+ return new NumberType();
default:
- return TryResolveType(instance, codeType, codeProperty, typeContext);
+ return TryResolveType(codeType);
}
}
- private string TryResolveType(AttributeDecoratedInstance instance, CodeTypeRef codeType, CodeProperty codeProperty, TypeContext typeContext)
+ private TypescriptType TryResolveType(CodeTypeRef codeType)
{
if (codeType.TypeKind == vsCMTypeRef.vsCMTypeRefArray)
{
- string typeFullName = codeType.ElementType.AsFullName;
- return TryResolveEnumerableType(typeFullName, typeContext);
- }
-
- if (genericCollectionTypeStarts.Any(s => codeType.AsFullName.StartsWith(s)))
- {
- string fullName = UnwrapGenericType(codeType);
- return TryResolveEnumerableType(fullName, typeContext);
+ return new ArrayType()
+ {
+ ElementType = GetTypeScriptType(codeType.ElementType)
+ };
}
- return TryResolveUnknownType(codeType.AsFullName, typeContext);
+ return GetTypeScriptType(codeType.AsFullName);
}
- private string TryResolveEnumerableType(string typeFullName, TypeContext typeContext)
+ private ArrayType TryResolveEnumerableType(string typeFullName)
{
- return TryResolveUnknownType(typeFullName, typeContext) + "[]";
+ return new ArrayType
+ {
+ ElementType = GetTypeScriptType(typeFullName)
+ };
}
- private string TryResolveUnknownType(string typeFullName, TypeContext typeContext)
+ public TypescriptType GetTypeScriptType(string typeFullName)
{
- AttributeDecoratedInstance instance;
- if (typeContext.TryGetValue(typeFullName, out instance))
- return instance.CodeType.Name;
+ CustomType customType;
+ if (customTypes.TryGetValue(typeFullName, out customType))
+ return customType;
+
+ if (IsGenericEnumerable(typeFullName))
+ {
+ return new ArrayType
+ {
+ ElementType = GetTypeScriptType(UnwrapGenericType(typeFullName))
+ };
+ }
switch (typeFullName)
{
@@ -302,65 +445,247 @@ public class CodeGenerator
case "System.Byte":
case "System.SByte":
case "System.Single":
- return "number";
+ return new NumberType();
case "System.String":
case "System.DateTime":
- return "string";
-
- case "System.Object":
- return "Object";
+ return new StringType();
default:
- return "any";
+ return new TypescriptType();
}
}
- private string UnwrapGenericType(CodeTypeRef codeType)
+ public string UnwrapGenericType(string typeFullName)
{
- return UnwrapGenericType(codeType.AsFullName);
+ int firstIndex = typeFullName.IndexOf('<');
+ return typeFullName.Substring(firstIndex+1, typeFullName.Length - firstIndex- 2);
}
- private string UnwrapGenericType(string typeFullName)
+ public bool IsGenericEnumerable(string typeFullName)
{
- return typeFullName.Split('<', '>')[1];
+ return genericCollectionTypeStarts.Any(t => typeFullName.StartsWith(t));
}
}
-public class TypeContext: Dictionary
+// -- Code generation --------------------------------------------------------------------------
+
+class CodeGenerator
{
- public TypeContext(IDictionary dictionary): base(dictionary)
+ public Project Project { get; private set; }
+ public Settings Settings { get; private set; }
+
+ private static readonly string InterfaceAttributeFullName = "T4TS.TypeScriptInterfaceAttribute";
+ private static readonly string MemberAttributeFullName = "T4TS.TypeScriptMemberAttribute";
+
+ public CodeGenerator(Project project, Settings settings)
{
+ if (project == null)
+ throw new ArgumentNullException("project");
+
+ if (settings == null)
+ throw new ArgumentNullException("settings");
+ this.Project = project;
+ this.Settings = settings;
}
-}
-
-public class TypeScriptInterface
-{
- public string Name { get; set; }
- public string FullName { get; set; }
- public List Members { get; set; }
- public string IndexerType { get; set; }
-}
-
-public class AttributeDecoratedInstance
-{
- public CodeNamespace Namespace { get; set; }
- public CodeType CodeType { get; set; }
- public string TypescriptType { get; set; }
-}
-public class TypeScriptInterfaceMember
-{
- public string Name { get; set; }
- public string Type { get; set; }
+ public TypeContext BuildContext()
+ {
+ var typeContext = new TypeContext();
+
+ new ProjectTraverser(this.Project, (ns) =>
+ {
+ new NamespaceTraverser(ns, (codeClass) =>
+ {
+ CodeAttribute attribute;
+ if (!TryGetAttribute(codeClass.Attributes, InterfaceAttributeFullName, out attribute))
+ return;
+
+ var values = GetInterfaceValues(codeClass, attribute);
+ var customType = new CustomType(values.Name, values.Module);
+
+ typeContext.AddCustomType(codeClass.FullName, customType);
+ });
+ });
+
+ return typeContext;
+ }
+
+ public IEnumerable GetAllInterfaces()
+ {
+ var typeContext = BuildContext();
+ var byModuleName = new Dictionary();
+
+ new ProjectTraverser(this.Project, (ns) =>
+ {
+ new NamespaceTraverser(ns, (codeClass) =>
+ {
+ if (codeClass.Attributes == null || codeClass.Attributes.Count == 0)
+ return;
+
+ CodeAttribute attribute;
+ if (!TryGetAttribute(codeClass.Attributes, InterfaceAttributeFullName, out attribute))
+ return;
+
+ var values = GetInterfaceValues(codeClass, attribute);
+
+ TypeScriptModule module;
+ if (!byModuleName.TryGetValue(values.Module, out module))
+ {
+ module = new TypeScriptModule { QualifiedName = values.Module };
+ byModuleName.Add(values.Module, module);
+ }
+
+ module.Interfaces.Add(BuildInterface(codeClass, values, typeContext));
+ });
+ });
+
+ return byModuleName.Values
+ .OrderBy(m => m.QualifiedName)
+ .ToList();
+ }
+
+ private TypeScriptInterface BuildInterface(CodeClass codeClass, TypeScriptInterfaceAttributeValues attributeValues, TypeContext typeContext)
+ {
+ var tsInterface = new TypeScriptInterface
+ {
+ FullName = codeClass.FullName,
+ Name = attributeValues.Name
+ };
+
+ TypescriptType indexedType;
+ if (TryGetIndexedType(codeClass, typeContext, out indexedType))
+ tsInterface.IndexedType = indexedType;
+
+ new ClassTraverser(codeClass, (property) =>
+ {
+ TypeScriptInterfaceMember member;
+ if (TryGetMember(property, typeContext, out member))
+ tsInterface.Members.Add(member);
+ });
+ return tsInterface;
+ }
+
+ private bool TryGetAttribute(CodeElements attributes, string attributeFullName, out CodeAttribute attribute)
+ {
+ foreach (CodeAttribute attr in attributes)
+ {
+ if (attr.FullName == attributeFullName)
+ {
+ attribute = attr;
+ return true;
+ }
+ }
+
+ attribute = null;
+ return false;
+ }
+
+ private bool TryGetIndexedType(CodeClass codeClass, TypeContext typeContext, out TypescriptType indexedType)
+ {
+ indexedType = null;
+ if (codeClass.Bases == null || codeClass.Bases.Count == 0)
+ return false;
+
+ foreach (CodeElement baseClass in codeClass.Bases)
+ {
+ if (typeContext.IsGenericEnumerable(baseClass.FullName))
+ {
+ string fullName = typeContext.UnwrapGenericType(baseClass.FullName);
+ indexedType = typeContext.GetTypeScriptType(fullName);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private TypeScriptInterfaceAttributeValues GetInterfaceValues(CodeClass codeClass, CodeAttribute interfaceAttribute)
+ {
+ var values = GetAttributeValues(interfaceAttribute);
+
+ return new TypeScriptInterfaceAttributeValues
+ {
+ Name = values.ContainsKey("Name") ? values["Name"] : codeClass.Name,
+ Module = values.ContainsKey("Module") ? values["Module"] : Settings.DefaultModule ?? "T4TS",
+ };
+ }
+
+ private bool TryGetMember(CodeProperty property, TypeContext typeContext, out TypeScriptInterfaceMember member)
+ {
+ member = null;
+ if (property.Access != vsCMAccess.vsCMAccessPublic)
+ return false;
+
+ var getter = property.Getter;
+ if (getter == null)
+ return false;
+
+ var values = GetMemberValues(property, typeContext);
+ member = new TypeScriptInterfaceMember
+ {
+ Name = values.Name ?? property.Name,
+ FullName = property.FullName,
+ Optional = values.Optional,
+ Type = (string.IsNullOrWhiteSpace(values.Type))
+ ? typeContext.GetTypeScriptType(getter.Type)
+ : new CustomType(values.Type)
+ };
+
+ return true;
+ }
+
+ private TypeScriptMemberAttributeValues GetMemberValues(CodeProperty property, TypeContext typeContext)
+ {
+ bool? attributeOptional = null;
+ string attributeName = null;
+ string attributeType = null;
+
+ CodeAttribute attribute;
+ if (TryGetAttribute(property.Attributes, MemberAttributeFullName, out attribute))
+ {
+ var values = GetAttributeValues(attribute);
+ if (values.ContainsKey("Optional"))
+ attributeOptional = values["Optional"] == "true";
+
+ values.TryGetValue("Name", out attributeName);
+ values.TryGetValue("Type", out attributeType);
+ }
+
+ return new TypeScriptMemberAttributeValues
+ {
+ Optional = attributeOptional.HasValue ? attributeOptional.Value : Settings.DefaultOptional,
+ Name = attributeName,
+ Type = attributeType
+ };
+ }
+
+ private Dictionary GetAttributeValues(CodeAttribute codeAttribute)
+ {
+ var values = new Dictionary();
+ foreach (CodeElement child in codeAttribute.Children)
+ {
+ var property = (EnvDTE80.CodeAttributeArgument)child;
+ if (property == null || property.Value == null)
+ continue;
+
+ // remove quotes if the property is a string
+ string val = property.Value ?? string.Empty;
+ if (val.StartsWith("\"") && val.EndsWith("\""))
+ val = val.Substring(1, val.Length - 2);
+
+ values.Add(property.Name, val);
+ }
+
+ return values;
+ }
}
+// ------------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------
// https://raw.github.com/damieng/DamienGKit
// http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited
-// -------------------------------------------------------------------------------
// Manager class records the various blocks so it can split them up
class Manager {
diff --git a/build/T4TS.tt.settings.t4 b/build/T4TS.tt.settings.t4
new file mode 100644
index 0000000..43965d5
--- /dev/null
+++ b/build/T4TS.tt.settings.t4
@@ -0,0 +1,18 @@
+<#+
+/************************************************
+ Settings for T4TS.tt
+************************************************/
+
+// The default module of the generated interface. If a module is
+// not specified by the TypeScriptInterfaceAttribute, the interface
+// will belong to this module (may be empty, in which case the
+// interface will be globally accessible).
+const string DefaultModule = "T4TS";
+
+// The default value for the Optional flag for an interface member.
+// If not specified by the TypeScriptInterfaceAttribute, the Optional
+// flag will be set to this value. If the Optional flag is true, the
+// generated member will look like "member?: type" instead of "member: type".
+const bool DefaultOptional = false;
+
+#>
\ No newline at end of file