From 2a704b092a67e2bc6902063cfee7bcecaae2626b Mon Sep 17 00:00:00 2001 From: Christoffer Skeppstedt Date: Sat, 19 Apr 2014 18:42:17 +0200 Subject: [PATCH] Building version 2.1.0 --- T4TS.Build/T4TS.d.ts | 8 +- T4TS.Build/T4TS.tt | 279 ++++++++++++++++++++++++--------- T4TS.Build/T4TS.tt.settings.t4 | 57 ++++--- build/T4TS.Attributes.dll | Bin 5632 -> 5632 bytes build/T4TS.tt | 278 +++++++++++++++++++++++--------- build/T4TS.tt.settings.t4 | 57 ++++--- 6 files changed, 481 insertions(+), 198 deletions(-) diff --git a/T4TS.Build/T4TS.d.ts b/T4TS.Build/T4TS.d.ts index e15c149..96450d6 100644 --- a/T4TS.Build/T4TS.d.ts +++ b/T4TS.Build/T4TS.d.ts @@ -12,10 +12,11 @@ } // -- End global interfaces -module Fooz { +declare module Fooz { /** Generated from T4TS.Example.Models.Foobar **/ export interface IFoobar { - OverrideAll?: bool; + OverrideAll?: any; + AGuid: string; Recursive: Fooz.IFoobar; NullableInt?: number; NullableDouble?: number; @@ -24,10 +25,11 @@ module Fooz { TwoDimensions: string[][]; ThreeDimensions: Barfoo[][][]; camelCasePlease: number; + DoNotIgnoreMe: number; } } -module T4TS { +declare module T4TS { /** Generated from T4TS.Example.Models.InheritanceTest1 **/ export interface InheritanceTest1 extends Barfoo { SomeString: string; diff --git a/T4TS.Build/T4TS.tt b/T4TS.Build/T4TS.tt index a4d601c..1628642 100644 --- a/T4TS.Build/T4TS.tt +++ b/T4TS.Build/T4TS.tt @@ -10,9 +10,17 @@ <#@ import namespace="EnvDTE" #> <#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #> <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> -<#@ Include File="T4TS.tt.settings.t4" #> -<#= - OutputFormatter.GetOutput(GetDataToRender()) #><#+ +<#@ Include File="T4TS.tt.settings.t4" #><#= + OutputFormatter.GetOutput(GetDataToRender(), GetSettings()) #><#+ + +Settings settings = null; +Settings GetSettings() +{ + if (settings== null) + settings = Settings.Parse(SettingsValues); + + return settings; +} List GetDataToRender() { DTE dte = null; @@ -31,17 +39,7 @@ List GetDataToRender() { 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, - DefaultCamelCaseMemberNames = DefaultCamelCaseMemberNames, - DefaultInterfaceNamePrefix = DefaultInterfaceNamePrefix - }; - - var generator = new CodeTraverser(project, settings); - + var generator = new CodeTraverser(project, GetSettings()); return generator.GetAllInterfaces().ToList(); } @@ -62,12 +60,12 @@ Project GetProjectContainingT4File(DTE dte) { return projectItem.ContainingProject; } -public class InterfaceOutputAppender : OutputAppender + public class InterfaceOutputAppender : OutputAppender { private bool InGlobalModule { get; set; } - public InterfaceOutputAppender(StringBuilder output, int baseIndentation, bool inGlobalModule) - : base(output, baseIndentation) + public InterfaceOutputAppender(StringBuilder output, int baseIndentation, Settings settings, bool inGlobalModule) + : base(output, baseIndentation, settings) { this.InGlobalModule = inGlobalModule; } @@ -86,7 +84,7 @@ public class InterfaceOutputAppender : OutputAppender private void AppendMembers(TypeScriptInterface tsInterface) { - var appender = new MemberOutputAppender(Output, BaseIndentation + 4); + var appender = new MemberOutputAppender(Output, BaseIndentation + 4, Settings); foreach (var member in tsInterface.Members) appender.AppendOutput(member); } @@ -121,8 +119,8 @@ public class InterfaceOutputAppender : OutputAppender public class MemberOutputAppender : OutputAppender { - public MemberOutputAppender(StringBuilder output, int baseIndentation) - : base(output, baseIndentation) + public MemberOutputAppender(StringBuilder output, int baseIndentation, Settings settings) + : base(output, baseIndentation, settings) { } @@ -131,21 +129,30 @@ public class InterfaceOutputAppender : OutputAppender AppendIndendation(); bool isOptional = member.Optional || (member.Type is NullableType); + string type = member.Type.ToString(); + + if (member.Type is BoolType) + { + if (Settings.CompatibilityVersion != null && Settings.CompatibilityVersion < new Version(0, 9, 0)) + type = "bool"; + else + type = "boolean"; + } Output.AppendFormat("{0}{1}: {2}", member.Name, (isOptional ? "?" : ""), - member.Type + type ); Output.AppendLine(";"); } } - + public class ModuleOutputAppender : OutputAppender { - public ModuleOutputAppender(StringBuilder output, int baseIndentation) - : base(output, baseIndentation) + public ModuleOutputAppender(StringBuilder output, int baseIndentation, Settings settings) + : base(output, baseIndentation, settings) { } @@ -153,7 +160,7 @@ public class InterfaceOutputAppender : OutputAppender { BeginModule(module); - var interfaceAppender = new InterfaceOutputAppender(Output, BaseIndentation + 4, module.IsGlobal); + var interfaceAppender = new InterfaceOutputAppender(Output, BaseIndentation + 4, Settings, module.IsGlobal); foreach (var tsInterface in module.Interfaces) interfaceAppender.AppendOutput(tsInterface); @@ -168,7 +175,11 @@ public class InterfaceOutputAppender : OutputAppender } else { - Output.Append("module "); + if (Settings.CompatibilityVersion != null && Settings.CompatibilityVersion < new Version(0, 9, 0)) + Output.Append("module "); + else + Output.Append("declare module "); + Output.Append(module.QualifiedName); Output.AppendLine(" {"); } @@ -182,19 +193,24 @@ public class InterfaceOutputAppender : OutputAppender Output.AppendLine("}"); } } - + public abstract class OutputAppender where TSegment: class { protected StringBuilder Output { get; private set; } protected int BaseIndentation { get; private set; } + protected Settings Settings { get; private set; } - public OutputAppender(StringBuilder output, int baseIndentation) + public OutputAppender(StringBuilder output, int baseIndentation, Settings settings) { if (output == null) throw new ArgumentNullException("output"); + + if (settings == null) + throw new ArgumentNullException("settings"); this.Output = output; this.BaseIndentation = baseIndentation; + this.Settings = settings; } public abstract void AppendOutput(TSegment segment); @@ -221,10 +237,10 @@ public class InterfaceOutputAppender : OutputAppender return Output.ToString(); } } - + public static class OutputFormatter { - public static string GetOutput(List modules) + public static string GetOutput(List modules, Settings settings) { var output = new StringBuilder(); @@ -232,7 +248,7 @@ public class InterfaceOutputAppender : OutputAppender output.AppendLine(" Generated by T4TS.tt - don't make any changes in this file"); output.AppendLine("****************************************************************************/"); - var moduleAppender = new ModuleOutputAppender(output, 0); + var moduleAppender = new ModuleOutputAppender(output, 0, settings); foreach (var module in modules) { output.AppendLine(); @@ -242,30 +258,7 @@ public class InterfaceOutputAppender : OutputAppender return output.ToString(); } } - - public class Settings - { - /// - /// 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; } - - /// - /// The default value for the CamelCase flag for an interface member name, if not specified by the TypeScriptMemberAttribute - /// - public bool DefaultCamelCaseMemberNames { get; set; } - /// - /// The default string to prefix interface names with. For instance, you might want to prefix the names with an "I" to get conventional interface names. - /// - public string DefaultInterfaceNamePrefix { get; set; } - } - public class TypeScriptInterface { public string Name { get; set; } @@ -281,28 +274,30 @@ public class InterfaceOutputAppender : OutputAppender Members = new List(); } } - + public class TypeScriptInterfaceAttributeValues { public string Module { get; set; } public string Name { get; set; } public string NamePrefix { get; set; } } - + public class TypeScriptInterfaceMember { public string Name { get; set; } public TypescriptType Type { get; set; } public bool Optional { get; set; } public string FullName { get; set; } + public bool Ignore { get; set; } } - + public class TypeScriptMemberAttributeValues { public string Name { get; set; } public bool Optional { get; set; } public string Type { get; set; } public bool CamelCase { get; set; } + public bool Ignore { get; set; } } public class TypeScriptModule @@ -323,7 +318,7 @@ public class InterfaceOutputAppender : OutputAppender Interfaces = new List(); } } - + public class ClassTraverser { public CodeClass CodeClass { get; private set; } @@ -350,7 +345,7 @@ public class InterfaceOutputAppender : OutputAppender WithProperty(property); } } - + public class NamespaceTraverser { public Action WithCodeClass { get; private set; } @@ -375,7 +370,7 @@ public class InterfaceOutputAppender : OutputAppender WithCodeClass(codeClass); } } - + public class ProjectTraverser { public Action WithNamespace { get; private set; } @@ -410,7 +405,7 @@ public class InterfaceOutputAppender : OutputAppender } } } - + public class ArrayType: TypescriptType { public TypescriptType ElementType { get; set; } @@ -420,15 +415,31 @@ public class InterfaceOutputAppender : OutputAppender return ElementType.ToString() + "[]"; } } - + public class BoolType: TypescriptType { public override string Name { - get { return "bool"; } + get { return "boolean"; } } } - + + public class DateTimeType : TypescriptType + { + public override string Name + { + get { return "Date"; } + } + } + + public class GuidType : TypescriptType + { + public override string Name + { + get { return "string"; } + } + } + public class InterfaceType : TypescriptType { public TypeScriptInterfaceAttributeValues AttributeValues { get; private set; } @@ -476,7 +487,7 @@ public class InterfaceOutputAppender : OutputAppender return QualifedModule + "." + base.ToString(); } } - + public class NullableType : TypescriptType { public TypescriptType WrappedType { get; set; } @@ -486,7 +497,7 @@ public class InterfaceOutputAppender : OutputAppender return WrappedType.ToString(); } } - + public class NumberType : TypescriptType { public override string Name @@ -494,7 +505,7 @@ public class InterfaceOutputAppender : OutputAppender get { return "number"; } } } - + public class StringType: TypescriptType { public override string Name @@ -512,7 +523,7 @@ public class InterfaceOutputAppender : OutputAppender return Name; } } - + public class CodeTraverser { public Project Project { get; private set; } @@ -535,7 +546,7 @@ public class InterfaceOutputAppender : OutputAppender public TypeContext BuildContext() { - var typeContext = new TypeContext(); + var typeContext = new TypeContext(this.Settings); var partialClasses = new Dictionary(); new ProjectTraverser(this.Project, (ns) => @@ -590,9 +601,19 @@ public class InterfaceOutputAppender : OutputAppender var tsInterfaces = tsMap.Values.ToList(); tsMap.Keys.ToList().ForEach(codeClass => { - var parent = tsInterfaces.LastOrDefault(intf => codeClass.IsDerivedFrom[intf.FullName] && intf.FullName != codeClass.FullName); - if (parent != null) - tsMap[codeClass].Parent = parent; + CodeElements baseClasses = codeClass.Bases; + if (baseClasses.Count > 0) + { + CodeElement baseClass = baseClasses.Item(1); + if (baseClass != null) + { + var parent = tsInterfaces.SingleOrDefault(intf => intf.FullName == baseClass.FullName); + if (parent != null) + { + tsMap[codeClass].Parent = parent; + } + } + } }); return byModuleName.Values @@ -692,11 +713,17 @@ public class InterfaceOutputAppender : OutputAppender Name = values.Name ?? property.Name, FullName = property.FullName, Optional = values.Optional, + Ignore = values.Ignore, Type = (string.IsNullOrWhiteSpace(values.Type)) ? typeContext.GetTypeScriptType(getter.Type) : new InterfaceType(values.Type) }; + if (member.Ignore) + { + return false; + } + if (values.CamelCase && values.Name == null) member.Name = member.Name.Substring(0, 1).ToLowerInvariant() + member.Name.Substring(1); @@ -707,6 +734,7 @@ public class InterfaceOutputAppender : OutputAppender { bool? attributeOptional = null; bool? attributeCamelCase = null; + bool attributeIgnore = false; string attributeName = null; string attributeType = null; @@ -720,6 +748,9 @@ public class InterfaceOutputAppender : OutputAppender if (values.ContainsKey("CamelCase")) attributeCamelCase = values["CamelCase"] == "true"; + if (values.ContainsKey("Ignore")) + attributeIgnore = values["Ignore"] == "true"; + values.TryGetValue("Name", out attributeName); values.TryGetValue("Type", out attributeType); } @@ -729,7 +760,8 @@ public class InterfaceOutputAppender : OutputAppender Optional = attributeOptional.HasValue ? attributeOptional.Value : Settings.DefaultOptional, Name = attributeName, Type = attributeType, - CamelCase = attributeCamelCase ?? Settings.DefaultCamelCaseMemberNames + CamelCase = attributeCamelCase ?? Settings.DefaultCamelCaseMemberNames, + Ignore = attributeIgnore }; } @@ -753,14 +785,99 @@ public class InterfaceOutputAppender : OutputAppender return values; } } - + + public class Settings + { + /// + /// 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; } + + /// + /// The default value for the CamelCase flag for an interface member name, if not specified by the TypeScriptMemberAttribute + /// + public bool DefaultCamelCaseMemberNames { get; set; } + + /// + /// The default string to prefix interface names with. For instance, you might want to prefix the names with an "I" to get conventional interface names. + /// + public string DefaultInterfaceNamePrefix { get; set; } + + /// + /// The version of Typescript that is targeted + /// + public Version CompatibilityVersion { get; set; } + + /// + /// If true translates System.DateTime to native date + /// + public bool UseNativeDates { get; set; } + + public static Settings Parse(Dictionary settingsValues) + { + // Read settings from T4TS.tt.settings.tt + return new Settings + { + DefaultModule = ParseSettingReferenceType(settingsValues, "DefaultModule", s => s as string, "T4TS"), + DefaultOptional = ParseSettingNullableType(settingsValues, "DefaultOptional", false), + DefaultCamelCaseMemberNames = ParseSettingNullableType(settingsValues, "DefaultCamelCaseMemberNames", false), + DefaultInterfaceNamePrefix = ParseSettingReferenceType(settingsValues, "DefaultInterfaceNamePrefix", s => s as string, string.Empty), + CompatibilityVersion = ParseSettingReferenceType(settingsValues, "CompatibilityVersion", v => v as Version, new Version(0, 9, 1, 1)), + UseNativeDates = ParseSettingNullableType(settingsValues, "UseNativeDates", false) + }; + } + + private static T ParseSettingReferenceType(Dictionary settingsValues, string key, Func convert, T defaultValue) where T : class + { + object val; + if (settingsValues.TryGetValue(key, out val)) + return convert(val) ?? defaultValue; + + return defaultValue; + } + + private static T ParseSettingNullableType(Dictionary settingsValues, string key, T defaultValue) where T : struct + { + object val; + if (settingsValues.TryGetValue(key, out val)) + { + var nullable = val as Nullable; + if (nullable == null || !nullable.HasValue) + return defaultValue; + + return nullable.Value; + } + + return defaultValue; + } + + private static T ParseConfigValueType(Dictionary settingsValues, string key, Func convert, T defaultValue) + { + object val; + if (settingsValues.TryGetValue(key, out val)) + return convert(val); + + return defaultValue; + } + } + public class TypeContext { + public Settings Settings { get; private set; } + public TypeContext(Settings settings) + { + this.Settings = settings; + } + private static readonly string[] genericCollectionTypeStarts = new string[] { "System.Collections.Generic.List<", "System.Collections.Generic.IList<", - "System.Collections.Generic.ICollection<", - "System.Collections.Generic.IEnumerable<" + "System.Collections.Generic.ICollection<" }; private static readonly string nullableTypeStart = "System.Nullable<"; @@ -854,6 +971,9 @@ public class InterfaceOutputAppender : OutputAppender switch (typeFullName) { + case "System.Guid": + return new GuidType(); + case "System.Double": case "System.Int16": case "System.Int32": @@ -868,9 +988,14 @@ public class InterfaceOutputAppender : OutputAppender return new NumberType(); case "System.String": - case "System.DateTime": return new StringType(); + case "System.DateTime": + if (Settings.UseNativeDates) + return new DateTimeType(); + else + return new StringType(); + default: return new TypescriptType(); } diff --git a/T4TS.Build/T4TS.tt.settings.t4 b/T4TS.Build/T4TS.tt.settings.t4 index d3f697d..21c2cd0 100644 --- a/T4TS.Build/T4TS.tt.settings.t4 +++ b/T4TS.Build/T4TS.tt.settings.t4 @@ -1,27 +1,42 @@ <#+ -/************************************************ - Settings for T4TS.tt -************************************************/ +/// +/// These settings can be used to customize the output of T4TS. +/// The default for all settings are determined by T4TS.tt in ReadSettings(). +/// +readonly Dictionary SettingsValues = new Dictionary() +{ + // 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). + // Type: string, defaults to "T4TS" if not specified. + { "DefaultModule", null }, -// 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". + // Type: bool?, defaults to false if not specified. + { "DefaultOptional", null }, + + // The default value for the CamelCase flag for an interface member name. + // If set to true, the first character of member names will be lower cased. + // Type: bool?, defaults to false if not specified. + { "DefaultCamelCaseMemberNames", null }, -// 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; + // The default string to prefix interface names with. For instance, you + // might want to prefix the names with an "I" to get conventional + // interface names. + // Type: string, defaults to "" if not specified. + { "DefaultInterfaceNamePrefix", null }, -// The default value for the CamelCase flag for an interface member name. -// If set to true, the first character of member names will be lower cased. -const bool DefaultCamelCaseMemberNames = false; - -// The default string to prefix interface names with. For instance, you -// might want to prefix the names with an "I" to get conventional -// interface names. -const string DefaultInterfaceNamePrefix = ""; + // The version of Typescript that is targeted. This is required to handle + // breaking changes in the language grammar and/or compiler. + // Type: System.Version + { "CompatibilityVersion", null }, + // The default value for DateTime type translation. + // Type: bool?, defaults to false if not specified. + { "UseNativeDates", null } +}; #> \ No newline at end of file diff --git a/build/T4TS.Attributes.dll b/build/T4TS.Attributes.dll index d2f307ec75361b782b4dd2dbd9335b7d112279fa..6bebbb45cdcedaeb731a26b67dbc5cfc6558ad9c 100644 GIT binary patch delta 832 zcma))Pe{~36vw}Be!u$x0W)4Nj2l|iiN@`KKQ2Fs-ECtg5WNX3vpue~lN%o%&1BVc9lT;)hI`Dg_B3gog@@x0;0gQNwZ9MZveilx$ti1}4Ka-&M1*{1HH23w&UgDk zJDI<<-jrXh*h%6$`}i+xO+Oz)JQM@m1Z2cT>S!x?@%6pqC zGlRMFJ(a{LTxz*aa$MWF0MPGrIp>-h^4V*J-sdSXGW5Q8!`&#iDe$6H=_>1u3(EVVzc)zr9g{7 delta 718 zcmaiyO=uHA6vzK>cC*Qv?Dor#Xalw;k{Clxtyr+)p{*YwQbJ>_;>}Pb7JIN>RImwF zDB>Y;PJ-e|Z4ZL79$LW8;+rvd=k5I7f8H=}-p))pRZdl> z&Hghxhii8tG*p`%AxNUQZz%~xWT!~UvlOROvO<&F(<$h7f+^s`=MLb94Uqic^vay5 z_!F(brX=Jy5jzw!f#?mO%08@g{>tUj_=O3TVkQ9ahiD;>XDphPK3>QSvVR+p$Bg}x zewHe%JZD9WyJ(DE_SERSq1J$fizqVdv`Q9!Ylel=y_z}AlIE1=L(NZOPFPse{HY_Y z#%hw6^&9()KAbjG>RZOBVd0VHM?-D1rnzB^%1-mBQ=&MRag(+&@5!s?m+tUOwCSHD zEOW8{r=0O!%TB;YT^Pd#x)8+^W)bVm>u@<9#9h{HI^M3iS4VpAl;eFmlGV&(QC_kN za>+`{Kh`ei1@VRA742ZYlRf_UfZ*b6F3V;Ue278TK{kO99M%q-fQu&9O>AcQ-=TAY zMqBV^{Zk|d?5uojhrLDnqzJ?4(6f7n7UqkU^O-l_4~p@Y=VM+XbbwOop{Z$gkr6(W zIu}(B0mJ#h{MPQ*33Fw4X72X=E$`2bta#H-fSg3*&8byKz(%!sVAo <#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #> <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> -<#@ Include File="T4TS.tt.settings.t4" #> -<#= - OutputFormatter.GetOutput(GetDataToRender()) #><#+ +<#@ Include File="T4TS.tt.settings.t4" #><#= + OutputFormatter.GetOutput(GetDataToRender(), GetSettings()) #><#+ + +Settings settings = null; +Settings GetSettings() +{ + if (settings== null) + settings = Settings.Parse(SettingsValues); + + return settings; +} List GetDataToRender() { DTE dte = null; @@ -31,17 +39,7 @@ List GetDataToRender() { 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, - DefaultCamelCaseMemberNames = DefaultCamelCaseMemberNames, - DefaultInterfaceNamePrefix = DefaultInterfaceNamePrefix - }; - - var generator = new CodeTraverser(project, settings); - + var generator = new CodeTraverser(project, GetSettings()); return generator.GetAllInterfaces().ToList(); } @@ -62,12 +60,12 @@ Project GetProjectContainingT4File(DTE dte) { return projectItem.ContainingProject; } -public class InterfaceOutputAppender : OutputAppender + public class InterfaceOutputAppender : OutputAppender { private bool InGlobalModule { get; set; } - public InterfaceOutputAppender(StringBuilder output, int baseIndentation, bool inGlobalModule) - : base(output, baseIndentation) + public InterfaceOutputAppender(StringBuilder output, int baseIndentation, Settings settings, bool inGlobalModule) + : base(output, baseIndentation, settings) { this.InGlobalModule = inGlobalModule; } @@ -86,7 +84,7 @@ public class InterfaceOutputAppender : OutputAppender private void AppendMembers(TypeScriptInterface tsInterface) { - var appender = new MemberOutputAppender(Output, BaseIndentation + 4); + var appender = new MemberOutputAppender(Output, BaseIndentation + 4, Settings); foreach (var member in tsInterface.Members) appender.AppendOutput(member); } @@ -121,8 +119,8 @@ public class InterfaceOutputAppender : OutputAppender public class MemberOutputAppender : OutputAppender { - public MemberOutputAppender(StringBuilder output, int baseIndentation) - : base(output, baseIndentation) + public MemberOutputAppender(StringBuilder output, int baseIndentation, Settings settings) + : base(output, baseIndentation, settings) { } @@ -131,21 +129,30 @@ public class InterfaceOutputAppender : OutputAppender AppendIndendation(); bool isOptional = member.Optional || (member.Type is NullableType); + string type = member.Type.ToString(); + + if (member.Type is BoolType) + { + if (Settings.CompatibilityVersion != null && Settings.CompatibilityVersion < new Version(0, 9, 0)) + type = "bool"; + else + type = "boolean"; + } Output.AppendFormat("{0}{1}: {2}", member.Name, (isOptional ? "?" : ""), - member.Type + type ); Output.AppendLine(";"); } } - + public class ModuleOutputAppender : OutputAppender { - public ModuleOutputAppender(StringBuilder output, int baseIndentation) - : base(output, baseIndentation) + public ModuleOutputAppender(StringBuilder output, int baseIndentation, Settings settings) + : base(output, baseIndentation, settings) { } @@ -153,7 +160,7 @@ public class InterfaceOutputAppender : OutputAppender { BeginModule(module); - var interfaceAppender = new InterfaceOutputAppender(Output, BaseIndentation + 4, module.IsGlobal); + var interfaceAppender = new InterfaceOutputAppender(Output, BaseIndentation + 4, Settings, module.IsGlobal); foreach (var tsInterface in module.Interfaces) interfaceAppender.AppendOutput(tsInterface); @@ -168,7 +175,11 @@ public class InterfaceOutputAppender : OutputAppender } else { - Output.Append("module "); + if (Settings.CompatibilityVersion != null && Settings.CompatibilityVersion < new Version(0, 9, 0)) + Output.Append("module "); + else + Output.Append("declare module "); + Output.Append(module.QualifiedName); Output.AppendLine(" {"); } @@ -182,19 +193,24 @@ public class InterfaceOutputAppender : OutputAppender Output.AppendLine("}"); } } - + public abstract class OutputAppender where TSegment: class { protected StringBuilder Output { get; private set; } protected int BaseIndentation { get; private set; } + protected Settings Settings { get; private set; } - public OutputAppender(StringBuilder output, int baseIndentation) + public OutputAppender(StringBuilder output, int baseIndentation, Settings settings) { if (output == null) throw new ArgumentNullException("output"); + + if (settings == null) + throw new ArgumentNullException("settings"); this.Output = output; this.BaseIndentation = baseIndentation; + this.Settings = settings; } public abstract void AppendOutput(TSegment segment); @@ -221,10 +237,10 @@ public class InterfaceOutputAppender : OutputAppender return Output.ToString(); } } - + public static class OutputFormatter { - public static string GetOutput(List modules) + public static string GetOutput(List modules, Settings settings) { var output = new StringBuilder(); @@ -232,7 +248,7 @@ public class InterfaceOutputAppender : OutputAppender output.AppendLine(" Generated by T4TS.tt - don't make any changes in this file"); output.AppendLine("****************************************************************************/"); - var moduleAppender = new ModuleOutputAppender(output, 0); + var moduleAppender = new ModuleOutputAppender(output, 0, settings); foreach (var module in modules) { output.AppendLine(); @@ -242,30 +258,7 @@ public class InterfaceOutputAppender : OutputAppender return output.ToString(); } } - - public class Settings - { - /// - /// 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; } - - /// - /// The default value for the CamelCase flag for an interface member name, if not specified by the TypeScriptMemberAttribute - /// - public bool DefaultCamelCaseMemberNames { get; set; } - /// - /// The default string to prefix interface names with. For instance, you might want to prefix the names with an "I" to get conventional interface names. - /// - public string DefaultInterfaceNamePrefix { get; set; } - } - public class TypeScriptInterface { public string Name { get; set; } @@ -281,28 +274,30 @@ public class InterfaceOutputAppender : OutputAppender Members = new List(); } } - + public class TypeScriptInterfaceAttributeValues { public string Module { get; set; } public string Name { get; set; } public string NamePrefix { get; set; } } - + public class TypeScriptInterfaceMember { public string Name { get; set; } public TypescriptType Type { get; set; } public bool Optional { get; set; } public string FullName { get; set; } + public bool Ignore { get; set; } } - + public class TypeScriptMemberAttributeValues { public string Name { get; set; } public bool Optional { get; set; } public string Type { get; set; } public bool CamelCase { get; set; } + public bool Ignore { get; set; } } public class TypeScriptModule @@ -323,7 +318,7 @@ public class InterfaceOutputAppender : OutputAppender Interfaces = new List(); } } - + public class ClassTraverser { public CodeClass CodeClass { get; private set; } @@ -350,7 +345,7 @@ public class InterfaceOutputAppender : OutputAppender WithProperty(property); } } - + public class NamespaceTraverser { public Action WithCodeClass { get; private set; } @@ -375,7 +370,7 @@ public class InterfaceOutputAppender : OutputAppender WithCodeClass(codeClass); } } - + public class ProjectTraverser { public Action WithNamespace { get; private set; } @@ -410,7 +405,7 @@ public class InterfaceOutputAppender : OutputAppender } } } - + public class ArrayType: TypescriptType { public TypescriptType ElementType { get; set; } @@ -420,15 +415,31 @@ public class InterfaceOutputAppender : OutputAppender return ElementType.ToString() + "[]"; } } - + public class BoolType: TypescriptType { public override string Name { - get { return "bool"; } + get { return "boolean"; } } } - + + public class DateTimeType : TypescriptType + { + public override string Name + { + get { return "Date"; } + } + } + + public class GuidType : TypescriptType + { + public override string Name + { + get { return "string"; } + } + } + public class InterfaceType : TypescriptType { public TypeScriptInterfaceAttributeValues AttributeValues { get; private set; } @@ -476,7 +487,7 @@ public class InterfaceOutputAppender : OutputAppender return QualifedModule + "." + base.ToString(); } } - + public class NullableType : TypescriptType { public TypescriptType WrappedType { get; set; } @@ -486,7 +497,7 @@ public class InterfaceOutputAppender : OutputAppender return WrappedType.ToString(); } } - + public class NumberType : TypescriptType { public override string Name @@ -494,7 +505,7 @@ public class InterfaceOutputAppender : OutputAppender get { return "number"; } } } - + public class StringType: TypescriptType { public override string Name @@ -512,7 +523,7 @@ public class InterfaceOutputAppender : OutputAppender return Name; } } - + public class CodeTraverser { public Project Project { get; private set; } @@ -535,7 +546,7 @@ public class InterfaceOutputAppender : OutputAppender public TypeContext BuildContext() { - var typeContext = new TypeContext(); + var typeContext = new TypeContext(this.Settings); var partialClasses = new Dictionary(); new ProjectTraverser(this.Project, (ns) => @@ -590,9 +601,19 @@ public class InterfaceOutputAppender : OutputAppender var tsInterfaces = tsMap.Values.ToList(); tsMap.Keys.ToList().ForEach(codeClass => { - var parent = tsInterfaces.LastOrDefault(intf => codeClass.IsDerivedFrom[intf.FullName] && intf.FullName != codeClass.FullName); - if (parent != null) - tsMap[codeClass].Parent = parent; + CodeElements baseClasses = codeClass.Bases; + if (baseClasses.Count > 0) + { + CodeElement baseClass = baseClasses.Item(1); + if (baseClass != null) + { + var parent = tsInterfaces.SingleOrDefault(intf => intf.FullName == baseClass.FullName); + if (parent != null) + { + tsMap[codeClass].Parent = parent; + } + } + } }); return byModuleName.Values @@ -692,11 +713,17 @@ public class InterfaceOutputAppender : OutputAppender Name = values.Name ?? property.Name, FullName = property.FullName, Optional = values.Optional, + Ignore = values.Ignore, Type = (string.IsNullOrWhiteSpace(values.Type)) ? typeContext.GetTypeScriptType(getter.Type) : new InterfaceType(values.Type) }; + if (member.Ignore) + { + return false; + } + if (values.CamelCase && values.Name == null) member.Name = member.Name.Substring(0, 1).ToLowerInvariant() + member.Name.Substring(1); @@ -707,6 +734,7 @@ public class InterfaceOutputAppender : OutputAppender { bool? attributeOptional = null; bool? attributeCamelCase = null; + bool attributeIgnore = false; string attributeName = null; string attributeType = null; @@ -720,6 +748,9 @@ public class InterfaceOutputAppender : OutputAppender if (values.ContainsKey("CamelCase")) attributeCamelCase = values["CamelCase"] == "true"; + if (values.ContainsKey("Ignore")) + attributeIgnore = values["Ignore"] == "true"; + values.TryGetValue("Name", out attributeName); values.TryGetValue("Type", out attributeType); } @@ -729,7 +760,8 @@ public class InterfaceOutputAppender : OutputAppender Optional = attributeOptional.HasValue ? attributeOptional.Value : Settings.DefaultOptional, Name = attributeName, Type = attributeType, - CamelCase = attributeCamelCase ?? Settings.DefaultCamelCaseMemberNames + CamelCase = attributeCamelCase ?? Settings.DefaultCamelCaseMemberNames, + Ignore = attributeIgnore }; } @@ -753,9 +785,95 @@ public class InterfaceOutputAppender : OutputAppender return values; } } - + + public class Settings + { + /// + /// 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; } + + /// + /// The default value for the CamelCase flag for an interface member name, if not specified by the TypeScriptMemberAttribute + /// + public bool DefaultCamelCaseMemberNames { get; set; } + + /// + /// The default string to prefix interface names with. For instance, you might want to prefix the names with an "I" to get conventional interface names. + /// + public string DefaultInterfaceNamePrefix { get; set; } + + /// + /// The version of Typescript that is targeted + /// + public Version CompatibilityVersion { get; set; } + + /// + /// If true translates System.DateTime to native date + /// + public bool UseNativeDates { get; set; } + + public static Settings Parse(Dictionary settingsValues) + { + // Read settings from T4TS.tt.settings.tt + return new Settings + { + DefaultModule = ParseSettingReferenceType(settingsValues, "DefaultModule", s => s as string, "T4TS"), + DefaultOptional = ParseSettingNullableType(settingsValues, "DefaultOptional", false), + DefaultCamelCaseMemberNames = ParseSettingNullableType(settingsValues, "DefaultCamelCaseMemberNames", false), + DefaultInterfaceNamePrefix = ParseSettingReferenceType(settingsValues, "DefaultInterfaceNamePrefix", s => s as string, string.Empty), + CompatibilityVersion = ParseSettingReferenceType(settingsValues, "CompatibilityVersion", v => v as Version, new Version(0, 9, 1, 1)), + UseNativeDates = ParseSettingNullableType(settingsValues, "UseNativeDates", false) + }; + } + + private static T ParseSettingReferenceType(Dictionary settingsValues, string key, Func convert, T defaultValue) where T : class + { + object val; + if (settingsValues.TryGetValue(key, out val)) + return convert(val) ?? defaultValue; + + return defaultValue; + } + + private static T ParseSettingNullableType(Dictionary settingsValues, string key, T defaultValue) where T : struct + { + object val; + if (settingsValues.TryGetValue(key, out val)) + { + var nullable = val as Nullable; + if (nullable == null || !nullable.HasValue) + return defaultValue; + + return nullable.Value; + } + + return defaultValue; + } + + private static T ParseConfigValueType(Dictionary settingsValues, string key, Func convert, T defaultValue) + { + object val; + if (settingsValues.TryGetValue(key, out val)) + return convert(val); + + return defaultValue; + } + } + public class TypeContext { + public Settings Settings { get; private set; } + public TypeContext(Settings settings) + { + this.Settings = settings; + } + private static readonly string[] genericCollectionTypeStarts = new string[] { "System.Collections.Generic.List<", "System.Collections.Generic.IList<", @@ -853,6 +971,9 @@ public class InterfaceOutputAppender : OutputAppender switch (typeFullName) { + case "System.Guid": + return new GuidType(); + case "System.Double": case "System.Int16": case "System.Int32": @@ -867,9 +988,14 @@ public class InterfaceOutputAppender : OutputAppender return new NumberType(); case "System.String": - case "System.DateTime": return new StringType(); + case "System.DateTime": + if (Settings.UseNativeDates) + return new DateTimeType(); + else + return new StringType(); + default: return new TypescriptType(); } @@ -891,4 +1017,4 @@ public class InterfaceOutputAppender : OutputAppender return genericCollectionTypeStarts.Any(t => typeFullName.StartsWith(t)); } } -#> \ No newline at end of file +#> diff --git a/build/T4TS.tt.settings.t4 b/build/T4TS.tt.settings.t4 index d3f697d..21c2cd0 100644 --- a/build/T4TS.tt.settings.t4 +++ b/build/T4TS.tt.settings.t4 @@ -1,27 +1,42 @@ <#+ -/************************************************ - Settings for T4TS.tt -************************************************/ +/// +/// These settings can be used to customize the output of T4TS. +/// The default for all settings are determined by T4TS.tt in ReadSettings(). +/// +readonly Dictionary SettingsValues = new Dictionary() +{ + // 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). + // Type: string, defaults to "T4TS" if not specified. + { "DefaultModule", null }, -// 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". + // Type: bool?, defaults to false if not specified. + { "DefaultOptional", null }, + + // The default value for the CamelCase flag for an interface member name. + // If set to true, the first character of member names will be lower cased. + // Type: bool?, defaults to false if not specified. + { "DefaultCamelCaseMemberNames", null }, -// 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; + // The default string to prefix interface names with. For instance, you + // might want to prefix the names with an "I" to get conventional + // interface names. + // Type: string, defaults to "" if not specified. + { "DefaultInterfaceNamePrefix", null }, -// The default value for the CamelCase flag for an interface member name. -// If set to true, the first character of member names will be lower cased. -const bool DefaultCamelCaseMemberNames = false; - -// The default string to prefix interface names with. For instance, you -// might want to prefix the names with an "I" to get conventional -// interface names. -const string DefaultInterfaceNamePrefix = ""; + // The version of Typescript that is targeted. This is required to handle + // breaking changes in the language grammar and/or compiler. + // Type: System.Version + { "CompatibilityVersion", null }, + // The default value for DateTime type translation. + // Type: bool?, defaults to false if not specified. + { "UseNativeDates", null } +}; #> \ No newline at end of file