diff --git a/src/TypeGen/TypeGen.Core/Generator/Generator.cs b/src/TypeGen/TypeGen.Core/Generator/Generator.cs index 35b56a69..df8279f5 100644 --- a/src/TypeGen/TypeGen.Core/Generator/Generator.cs +++ b/src/TypeGen/TypeGen.Core/Generator/Generator.cs @@ -4,8 +4,11 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; using System.Threading.Tasks; using TypeGen.Core.Conversion; +using TypeGen.Core.Converters; using TypeGen.Core.Extensions; using TypeGen.Core.Generator.Context; using TypeGen.Core.Generator.Services; @@ -428,6 +431,9 @@ private IEnumerable GenerateClass(Type type, ExportTsClassAttribute clas } string importsText = _tsContentGenerator.GetImportsText(type, outputDir); + + string constructorsText = GetClassConstructorsText(type); + string propertiesText = GetClassPropertiesText(type); // generate the file content @@ -445,8 +451,8 @@ private IEnumerable GenerateClass(Type type, ExportTsClassAttribute clas var tsDoc = GetTsDocForType(type); var content = _typeService.UseDefaultExport(type) ? - _templateService.FillClassDefaultExportTemplate(importsText, tsTypeName, tsTypeNameFirstPart, extendsText, implementsText, propertiesText, tsDoc, customHead, customBody, Options.FileHeading) : - _templateService.FillClassTemplate(importsText, tsTypeName, extendsText, implementsText, propertiesText, tsDoc, customHead, customBody, Options.FileHeading); + _templateService.FillClassDefaultExportTemplate(importsText, tsTypeName, tsTypeNameFirstPart, extendsText, implementsText, propertiesText, tsDoc, customHead, customBody, constructorsText, Options.FileHeading) : + _templateService.FillClassTemplate(importsText, tsTypeName, extendsText, implementsText, propertiesText, tsDoc, customHead, customBody, constructorsText, Options.FileHeading); // write TypeScript file FileContentGenerated?.Invoke(this, new FileContentGeneratedArgs(type, filePath, content)); @@ -644,6 +650,78 @@ private void LogClassPropertyWarnings(MemberInfo memberInfo) Logger.Log($"TsOptionalAttribute used for a class property ({memberInfo.DeclaringType?.FullName}.{memberInfo.Name}). The attribute will be ignored.", LogLevel.Warning); } + private string GetClassConstructorsText(Type type) + { + var constructorsText = ""; + var constructors = type.GetConstructors(); + + //var isRecord = ((TypeInfo)type).DeclaredProperties.FirstOrDefault(x => x.Name == "EqualityContract")?.GetMethod?.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is object; + + var classHasTsConstructorAttribute = type.GetCustomAttributes(typeof(TsConstructorAttribute)).Any(); + + var members = type.GetTsExportableMembers(_metadataReaderFactory.GetInstance()).Select(memberInfo => + { + var nameAttribute = _metadataReaderFactory.GetInstance().GetAttribute(memberInfo); + var memberName = nameAttribute?.Name ?? Options.PropertyNameConverters.Convert(memberInfo.Name, memberInfo); + var typeName = _typeService.GetTsTypeName(memberInfo); + Type memberType = null; + + switch (memberInfo.MemberType) + { + case MemberTypes.Field: + memberType = ((FieldInfo)memberInfo).FieldType; + break; + case MemberTypes.Property: + memberType = ((PropertyInfo)memberInfo).PropertyType; + break; + } + + return new TsMemberInfo(memberInfo.Name, memberType, memberName, typeName); + }).ToArray(); + + //if ctor params all match member names and types or is a Record + var matchedConstructors = constructors.Where(ctor => + (classHasTsConstructorAttribute || ctor.GetCustomAttributes(typeof(TsConstructorAttribute)).Any()) && + ctor.GetParameters().All(p => members.Any(m => m.DotNetMemberName.Equals(p.Name, StringComparison.InvariantCultureIgnoreCase) && m.DotNetMemberType == p.ParameterType))).ToArray(); + + //if single empty constructor, skip + if (matchedConstructors.Length == 1 && matchedConstructors[0].GetParameters().Length == 0) + { + return constructorsText; + } + + constructorsText += matchedConstructors.Aggregate(constructorsText, (current, ctorInfo) => current + GetClassConstructorText(ctorInfo, members)); + + return constructorsText; + } + + private string GetClassConstructorText(ConstructorInfo ctor, TsMemberInfo[] tsMemberTypes) + { + string argumentsText = ""; + string assignmentsText = ""; + + var parameters = ctor.GetParameters(); + + foreach (var param in parameters) + { + var matchingMemberInfo = tsMemberTypes.FirstOrDefault(k => k.DotNetMemberName.Equals(param.Name, StringComparison.InvariantCultureIgnoreCase)); + + if(matchingMemberInfo == null) + { + //not all parameters have a matching member name, skip constructor building + return string.Empty; + } + + argumentsText += _templateService.FillClassConstructorArgumentTemplate(matchingMemberInfo.TsMemberName, matchingMemberInfo.TsMemberType); + assignmentsText += _templateService.FillClassConstructorAssignmentTemplate(matchingMemberInfo.TsMemberName, matchingMemberInfo.TsMemberName); + } + + argumentsText = argumentsText.TrimEnd(',', ' '); + assignmentsText = RemoveLastLineEnding(assignmentsText); + + return _templateService.FillClassConstructorTemplate(argumentsText, assignmentsText); + } + /// /// Gets TypeScript class properties definition source code /// diff --git a/src/TypeGen/TypeGen.Core/Generator/GeneratorOptions.cs b/src/TypeGen/TypeGen.Core/Generator/GeneratorOptions.cs index 1aaacd4f..5d1c79eb 100644 --- a/src/TypeGen/TypeGen.Core/Generator/GeneratorOptions.cs +++ b/src/TypeGen/TypeGen.Core/Generator/GeneratorOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.Serialization; using TypeGen.Core.Converters; diff --git a/src/TypeGen/TypeGen.Core/Generator/Services/ITemplateService.cs b/src/TypeGen/TypeGen.Core/Generator/Services/ITemplateService.cs index d196ffd1..cb790e18 100644 --- a/src/TypeGen/TypeGen.Core/Generator/Services/ITemplateService.cs +++ b/src/TypeGen/TypeGen.Core/Generator/Services/ITemplateService.cs @@ -4,8 +4,8 @@ namespace TypeGen.Core.Generator.Services { internal interface ITemplateService { - string FillClassTemplate(string imports, string name, string extends, string implements, string properties, string tsDoc, string customHead, string customBody, string fileHeading = null); - string FillClassDefaultExportTemplate(string imports, string name, string exportName, string extends, string implements, string properties, string tsDoc, string customHead, string customBody, string fileHeading = null); + string FillClassTemplate(string imports, string name, string extends, string implements, string properties, string tsDoc, string customHead, string customBody, string constructorsText, string fileHeading = null); + string FillClassDefaultExportTemplate(string imports, string name, string exportName, string extends, string implements, string properties, string tsDoc, string customHead, string customBody, string constructorsText, string fileHeading = null); string FillClassPropertyTemplate(string modifiers, string name, string type, IEnumerable typeUnions, bool isOptional, string tsDoc, string defaultValue = null); string FillInterfaceTemplate(string imports, string name, string extends, string properties, string tsDoc, string customHead, string customBody, string fileHeading = null); string FillInterfaceDefaultExportTemplate(string imports, string name, string exportName, string extends, string properties, string tsDoc, string customHead, string customBody, string fileHeading = null); @@ -21,5 +21,8 @@ internal interface ITemplateService string GetExtendsText(string name); string GetExtendsText(IEnumerable names); string GetImplementsText(IEnumerable names); + string FillClassConstructorTemplate(string arguments, string assignments); + string FillClassConstructorArgumentTemplate(string argumentName, string argumentType); + string FillClassConstructorAssignmentTemplate(string memberName, string argumentName); } } \ No newline at end of file diff --git a/src/TypeGen/TypeGen.Core/Generator/Services/TemplateService.cs b/src/TypeGen/TypeGen.Core/Generator/Services/TemplateService.cs index 3ca1759e..646498b3 100644 --- a/src/TypeGen/TypeGen.Core/Generator/Services/TemplateService.cs +++ b/src/TypeGen/TypeGen.Core/Generator/Services/TemplateService.cs @@ -33,6 +33,9 @@ internal class TemplateService : ITemplateService private readonly string _indexTemplate; private readonly string _indexExportTemplate; private readonly string _headingTemplate; + private readonly string _classConstructorTemplate; + private readonly string _classConstructorArgumentTemplate; + private readonly string _classConstructorAssignmentTemplate; private GeneratorOptions GeneratorOptions => _generatorOptionsProvider.GeneratorOptions; @@ -58,10 +61,13 @@ public TemplateService(IInternalStorage internalStorage, IGeneratorOptionsProvid _indexTemplate = _internalStorage.GetEmbeddedResource("TypeGen.Core.Templates.Index.tpl"); _indexExportTemplate = _internalStorage.GetEmbeddedResource("TypeGen.Core.Templates.IndexExport.tpl"); _headingTemplate = _internalStorage.GetEmbeddedResource("TypeGen.Core.Templates.Heading.tpl"); + _classConstructorTemplate = _internalStorage.GetEmbeddedResource("TypeGen.Core.Templates.ClassConstructor.tpl"); + _classConstructorArgumentTemplate = _internalStorage.GetEmbeddedResource("TypeGen.Core.Templates.ClassConstructorArgument.tpl"); + _classConstructorAssignmentTemplate = _internalStorage.GetEmbeddedResource("TypeGen.Core.Templates.ClassConstructorAssignment.tpl"); } public string FillClassTemplate(string imports, string name, string extends, string implements, string properties, - string tsDoc, string customHead, string customBody, string fileHeading = null) + string tsDoc, string customHead, string customBody, string constructorsText, string fileHeading = null) { if (fileHeading == null) fileHeading = _headingTemplate; @@ -74,11 +80,12 @@ public string FillClassTemplate(string imports, string name, string extends, str .Replace(GetTag("tsDoc"), tsDoc) .Replace(GetTag("customHead"), customHead) .Replace(GetTag("customBody"), customBody) - .Replace(GetTag("fileHeading"), fileHeading); + .Replace(GetTag("fileHeading"), fileHeading) + .Replace(GetTag("constructors"), constructorsText); } public string FillClassDefaultExportTemplate(string imports, string name, string exportName, string extends, string implements, - string properties, string tsDoc, string customHead, string customBody, string fileHeading = null) + string properties, string tsDoc, string customHead, string customBody, string constructorsText, string fileHeading = null) { if (fileHeading == null) fileHeading = _headingTemplate; @@ -92,7 +99,8 @@ public string FillClassDefaultExportTemplate(string imports, string name, string .Replace(GetTag("tsDoc"), tsDoc) .Replace(GetTag("customHead"), customHead) .Replace(GetTag("customBody"), customBody) - .Replace(GetTag("fileHeading"), fileHeading); + .Replace(GetTag("fileHeading"), fileHeading) + .Replace(GetTag("constructors"), constructorsText); } public string FillClassPropertyTemplate(string modifiers, string name, string type, IEnumerable typeUnions, @@ -236,6 +244,27 @@ public string FillIndexExportTemplate(string filename) return ReplaceSpecialChars(_indexExportTemplate) .Replace(GetTag("filename"), filename); } + + public string FillClassConstructorTemplate(string arguments, string assignments) + { + return ReplaceSpecialChars(_classConstructorTemplate) + .Replace(GetTag("arguments"), arguments) + .Replace(GetTag("assignments"), assignments); + } + + public string FillClassConstructorArgumentTemplate(string argumentName, string argumentType) + { + return ReplaceSpecialChars(_classConstructorArgumentTemplate) + .Replace(GetTag("argName"), argumentName) + .Replace(GetTag("argType"), argumentType); + } + + public string FillClassConstructorAssignmentTemplate(string memberName, string argumentName) + { + return ReplaceSpecialChars(_classConstructorAssignmentTemplate) + .Replace(GetTag("memberName"), memberName) + .Replace(GetTag("argumentName"), argumentName); + } public string GetExtendsText(string name) => $" extends {name}"; public string GetExtendsText(IEnumerable names) => $" extends {string.Join(", ", names)}"; diff --git a/src/TypeGen/TypeGen.Core/Generator/TsMemberInfo.cs b/src/TypeGen/TypeGen.Core/Generator/TsMemberInfo.cs new file mode 100644 index 00000000..2d27042d --- /dev/null +++ b/src/TypeGen/TypeGen.Core/Generator/TsMemberInfo.cs @@ -0,0 +1,5 @@ +using System; + +namespace TypeGen.Core.Generator; + +public record TsMemberInfo(string DotNetMemberName, Type DotNetMemberType, string TsMemberName, string TsMemberType); \ No newline at end of file diff --git a/src/TypeGen/TypeGen.Core/Templates/Class.tpl b/src/TypeGen/TypeGen.Core/Templates/Class.tpl index f49dbd0f..c61127b8 100644 --- a/src/TypeGen/TypeGen.Core/Templates/Class.tpl +++ b/src/TypeGen/TypeGen.Core/Templates/Class.tpl @@ -1,3 +1,3 @@ $tg{fileHeading}$tg{imports}$tg{customHead}$tg{tsDoc}export class $tg{name}$tg{extends}$tg{implements} { -$tg{properties}$tg{customBody} +$tg{constructors}$tg{properties}$tg{customBody} } diff --git a/src/TypeGen/TypeGen.Core/Templates/ClassConstructor.tpl b/src/TypeGen/TypeGen.Core/Templates/ClassConstructor.tpl new file mode 100644 index 00000000..d6afcc41 --- /dev/null +++ b/src/TypeGen/TypeGen.Core/Templates/ClassConstructor.tpl @@ -0,0 +1,3 @@ +$tg{tab}constructor($tg{arguments}){ +$tg{assignments} +$tg{tab}} diff --git a/src/TypeGen/TypeGen.Core/Templates/ClassConstructorArgument.tpl b/src/TypeGen/TypeGen.Core/Templates/ClassConstructorArgument.tpl new file mode 100644 index 00000000..2a464a10 --- /dev/null +++ b/src/TypeGen/TypeGen.Core/Templates/ClassConstructorArgument.tpl @@ -0,0 +1 @@ +$tg{argName}: $tg{argType}, \ No newline at end of file diff --git a/src/TypeGen/TypeGen.Core/Templates/ClassConstructorAssignment.tpl b/src/TypeGen/TypeGen.Core/Templates/ClassConstructorAssignment.tpl new file mode 100644 index 00000000..c2ff5953 --- /dev/null +++ b/src/TypeGen/TypeGen.Core/Templates/ClassConstructorAssignment.tpl @@ -0,0 +1 @@ +$tg{tab}$tg{tab}this.$tg{memberName}: $tg{argumentName}; diff --git a/src/TypeGen/TypeGen.Core/Templates/ClassDefaultExport.tpl b/src/TypeGen/TypeGen.Core/Templates/ClassDefaultExport.tpl index 45a56f6b..839fae06 100644 --- a/src/TypeGen/TypeGen.Core/Templates/ClassDefaultExport.tpl +++ b/src/TypeGen/TypeGen.Core/Templates/ClassDefaultExport.tpl @@ -1,4 +1,4 @@ $tg{fileHeading}$tg{imports}$tg{customHead}$tg{tsDoc}class $tg{name}$tg{extends}$tg{implements} { -$tg{properties}$tg{customBody} +$tg{constructors}$tg{properties}$tg{customBody} } export default $tg{exportName}; diff --git a/src/TypeGen/TypeGen.Core/TypeAnnotations/TsConstructorAttribute.cs b/src/TypeGen/TypeGen.Core/TypeAnnotations/TsConstructorAttribute.cs new file mode 100644 index 00000000..9fa70c48 --- /dev/null +++ b/src/TypeGen/TypeGen.Core/TypeAnnotations/TsConstructorAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace TypeGen.Core.TypeAnnotations; + +/// +/// Identifies a TypeScript class constructor. +/// Constructor implementation will be generated if empty or if the arguments match members of the class. +/// ie: ctor(string name) => public string Name { get; set; } +/// generated as : +/// constructor(name: string) +/// { +/// this.Name: name; +/// } +/// +[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Class)] +public class TsConstructorAttribute: Attribute +{ + +} \ No newline at end of file diff --git a/src/TypeGen/TypeGen.Core/TypeGen.Core.csproj b/src/TypeGen/TypeGen.Core/TypeGen.Core.csproj index 57345189..51299660 100644 --- a/src/TypeGen/TypeGen.Core/TypeGen.Core.csproj +++ b/src/TypeGen/TypeGen.Core/TypeGen.Core.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;net7.0 TypeGen.Core.xml @@ -21,6 +21,9 @@ + + + @@ -39,7 +42,10 @@ - + + + + diff --git a/src/TypeGen/TypeGen.FileContentTest/Constructor/ConstructorGenerationTests.cs b/src/TypeGen/TypeGen.FileContentTest/Constructor/ConstructorGenerationTests.cs new file mode 100644 index 00000000..225bfd2c --- /dev/null +++ b/src/TypeGen/TypeGen.FileContentTest/Constructor/ConstructorGenerationTests.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using TypeGen.FileContentTest.TestingUtils; +using Xunit; + +namespace TypeGen.FileContentTest.Constructor; + +public class ConstructorGenerationTests: GenerationTestBase +{ + [Theory] + [InlineData(typeof(Entities.ClassWithConstructorMatchingMembers), "TypeGen.FileContentTest.Constructor.Expected.test-constructors.ts")] + [InlineData(typeof(Entities.ClassWithConstructorMatchingMembersMismatch), "TypeGen.FileContentTest.Constructor.Expected.test-constructors-mismatch.ts")] + [InlineData(typeof(Entities.ClassWithConstructorMatchingMembersAndEmpty), "TypeGen.FileContentTest.Constructor.Expected.test-empty-constructors.ts")] + [InlineData(typeof(Entities.MyRecord), "TypeGen.FileContentTest.Constructor.Expected.test-record-constructors.ts")] + public async Task TestConstructor(Type type, string expectedLocation) + { + await TestFromAssembly(type, expectedLocation); + } +} \ No newline at end of file diff --git a/src/TypeGen/TypeGen.FileContentTest/Constructor/Entities/ClassWithConstructorMatchingMembers.cs b/src/TypeGen/TypeGen.FileContentTest/Constructor/Entities/ClassWithConstructorMatchingMembers.cs new file mode 100644 index 00000000..45c0f4d9 --- /dev/null +++ b/src/TypeGen/TypeGen.FileContentTest/Constructor/Entities/ClassWithConstructorMatchingMembers.cs @@ -0,0 +1,57 @@ +using TypeGen.Core.TypeAnnotations; +using Xunit; + +namespace TypeGen.FileContentTest.Constructor.Entities; + +[ExportTsClass] +public class ClassWithConstructorMatchingMembers +{ + public string MyProp { get; set; } + public int myField; + public SubClass SubClass { get; set; } + + [TsConstructor] + public ClassWithConstructorMatchingMembers(string myProp, int myField, SubClass subClass) + { } +} + +[ExportTsClass] +public class ClassWithConstructorMatchingMembersMismatch +{ + public string MyProp { get; set; } + public int myField; + public SubClass SubClass { get; set; } + + [TsConstructor] + public ClassWithConstructorMatchingMembersMismatch(string myProp1, int myField, SubClass subClass) + { } +} + +[ExportTsClass] +[TsConstructor] +public class ClassWithConstructorMatchingMembersAndEmpty +{ + public string MyProp { get; set; } + public int myField; + public SubClass SubClass { get; set; } + + public ClassWithConstructorMatchingMembersAndEmpty(string myProp, int myField, SubClass subClass) + { } + + public ClassWithConstructorMatchingMembersAndEmpty() + { } +} + +[ExportTsClass] +[TsConstructor] +public record MyRecord(string Name); + +public class SubClass +{ + public string Name { get; set; } + + public SubClass(string name) + { + Name = name; + } +} \ No newline at end of file diff --git a/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-constructors-mismatch.ts b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-constructors-mismatch.ts new file mode 100644 index 00000000..82c4a6b4 --- /dev/null +++ b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-constructors-mismatch.ts @@ -0,0 +1,12 @@ +/** + * This is a TypeGen auto-generated file. + * Any changes made to this file can be lost when this file is regenerated. + */ + +import { SubClass } from "./sub-class"; + +export class ClassWithConstructorMatchingMembersMismatch { + myField: number; + myProp: string; + subClass: SubClass; +} diff --git a/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-constructors.ts b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-constructors.ts new file mode 100644 index 00000000..f82a91ab --- /dev/null +++ b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-constructors.ts @@ -0,0 +1,17 @@ +/** + * This is a TypeGen auto-generated file. + * Any changes made to this file can be lost when this file is regenerated. + */ + +import { SubClass } from "./sub-class"; + +export class ClassWithConstructorMatchingMembers { + constructor(myProp: string, myField: number, subClass: SubClass){ + this.myProp: myProp; + this.myField: myField; + this.subClass: subClass; + } + myField: number; + myProp: string; + subClass: SubClass; +} diff --git a/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-empty-constructors.ts b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-empty-constructors.ts new file mode 100644 index 00000000..ae2a97c6 --- /dev/null +++ b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-empty-constructors.ts @@ -0,0 +1,19 @@ +/** + * This is a TypeGen auto-generated file. + * Any changes made to this file can be lost when this file is regenerated. + */ + +import { SubClass } from "./sub-class"; + +export class ClassWithConstructorMatchingMembersAndEmpty { + constructor(myProp: string, myField: number, subClass: SubClass){ + this.myProp: myProp; + this.myField: myField; + this.subClass: subClass; + } + constructor(){ + } + myField: number; + myProp: string; + subClass: SubClass; +} diff --git a/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-record-constructors.ts b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-record-constructors.ts new file mode 100644 index 00000000..1616b6b8 --- /dev/null +++ b/src/TypeGen/TypeGen.FileContentTest/Constructor/Expected/test-record-constructors.ts @@ -0,0 +1,10 @@ +/** + * This is a TypeGen auto-generated file. + * Any changes made to this file can be lost when this file is regenerated. + */ +export class MyRecord { + constructor(name: string){ + this.name: name; + } + name: string; +} diff --git a/src/TypeGen/TypeGen.FileContentTest/TestingUtils/GenerationTestBase.cs b/src/TypeGen/TypeGen.FileContentTest/TestingUtils/GenerationTestBase.cs index 27dee9eb..17eb9a26 100644 --- a/src/TypeGen/TypeGen.FileContentTest/TestingUtils/GenerationTestBase.cs +++ b/src/TypeGen/TypeGen.FileContentTest/TestingUtils/GenerationTestBase.cs @@ -9,11 +9,14 @@ namespace TypeGen.FileContentTest.TestingUtils; public class GenerationTestBase { - protected async Task TestFromAssembly(Type type, string expectedLocation) + protected async Task TestFromAssembly(Type type, string expectedLocation, GeneratorOptions generatorOptions = null) { var readExpectedTask = EmbededResourceReader.GetEmbeddedResourceAsync(expectedLocation); - var generator = new Generator(); + var generator = generatorOptions != null ? + new Generator(generatorOptions) : + new Generator(); + var interceptor = GeneratorOutputInterceptor.CreateInterceptor(generator); await generator.GenerateAsync(type.Assembly); diff --git a/src/TypeGen/TypeGen.FileContentTest/TypeGen.FileContentTest.csproj b/src/TypeGen/TypeGen.FileContentTest/TypeGen.FileContentTest.csproj index 93719d7e..c64b8d1e 100644 --- a/src/TypeGen/TypeGen.FileContentTest/TypeGen.FileContentTest.csproj +++ b/src/TypeGen/TypeGen.FileContentTest/TypeGen.FileContentTest.csproj @@ -98,6 +98,9 @@ + + + @@ -115,6 +118,8 @@ + +