📜 ⬆️ ⬇️

Reinforced.Typings - more details

Hello again.
What about what, and I'm again about Reinforced.Typings - my library for generating TypeScript glue-code from C # -series, a brief introduction to which I did in a previous post . After that, he immediately received a number of questions and comments (not only on Habrahabr, by the way - many of those interested simply were not registered on it). For this, of course, many thanks to all, but based on the analyzed information, I realized that one brief post is not enough to describe how and what is realized. It turns out that people ask questions, “but is this supported?” And everyone has to explain the same thing time after time. So in this article I will make a small cheatsheet for attributes, fluent-configuration and tell you about additional features. In general, you are welcome. Beware, longrid and background information!


In the previous series


When I first talked about Reinforced.Typings, I casually mentioned that there are different attributes and they have a lot of all sorts of different custom properties, there are some code generators and something about the TypeScript assembly process. Everything is somehow messy, superficially, without details. Let's break it down. I am as concise as possible, but I’ll tell you in detail about the various configuration options, the build script, how it all works together, and what can be fashioned from all this.
In general, this article is a kind of manual / tutorial / cheatsheet for Reinforced.Typings, because I still do not have the time and resource to write full documentation in English. Of course, not everything is so sad - I already gathered my courage and wrote a whole plan of documentation. However, the “father of Russian democracy” was not enough for a larger (+ article on Habrahabr). So let's go.
PS: It is worth noting that I still counted a little and in the time that has passed since the last article was written, I finished some of the features in the library, fixed the bugs (so far without them). Therefore, the presented material is relevant from version 1.0.7.

How does it generally work?


You are relaxed, satisfied, you have warm feet and you write in C # and ASP.NET MVC using TypeScript. You install Reinforced.Typings from NuGet with the command
')
PM > Install-Package Reinforced.Typings 

Reinforced.Typings is immediately integrated into the build process of your application, starting at every build of the project. This moment is controlled from the Reinforced.Typings.settings.xml file, which is added to the root of your project — I call this the “build configuration”. Having started, Reinforced.Typings does its dark work by loading the build of your project and climbing into it through Reflection. The desired TypeScript glue-code (for example, TypeScript-interfaces corresponding to your server TransferObjects / View-models, transmitted to the client) is generated based on two possible factors:

Attribute and fluent configuration can be combined - this is not forbidden. They basically provide equal opportunities, but differ in flexibility.
In general, all the secret knowledge necessary to use Reinforced.Typings, is to navigate the parameters of its configuration. And there are only 3 setting points mentioned above - the assembly configuration (Reinforced.Typings.settings.xml), the attribute and fluent configuration. The build configuration determines the global parameters of the whole process, such as the files to which the generated code will be written, whether to export documentation, which assemblies to plug in, etc. Attribute and fluent configurations determine which code to generate and for which types.

Assembly configuration


Reinforced.Typings generates your taipings each time you rebuild a project. As stated above, it is embedded in the project building process. This is done through the mechanism of embedding .targets and .props files from the package that appeared in version NuGet 2.5.
By the way
The generator itself is in the rtcli.exe tool, which is launched using the included RtCli MSBuild. In short, everything is in the best tradition of calling custom tools during the build. In principle, nothing stops you from brazenly pulling out rtcli.exe and Reinforced.Typings.dll from the / tools directory of the package itself and using it at your discretion (as well as the RtCli task, which lies in /build/Reinforced.Typings.Integrate.dll) , but let's be objective - very few people will really do it.

Some parameters for starting the generator are in the Reinforced.Typings.settings.xml file, which is a piece of MSBuild script that connects to the .targets file of the package itself. Reinforced.Typings.settings.xml is added to the root of your project when the package is installed. By default it looks like this . I would not argue that it is well documented (mainly because of the brokenness of my English), so I’m including a cheatsheet describing configuration parameters:
Parameters in Reinforced.Typings.settings.xml
RtTargetFile (string)
Full path to the file to which all generated taypings will be written. In it (as in all other parameters), you can use MSBuild variables, including those specific to Microsoft.Common.CurrentVersion.targets. This parameter is key and mandatory when you just need to throw tapping for your project into one file, but it is not used if the function of splitting the generated code into many files is enabled (see below). Do not forget to add this file to your project by hand!

RtConfigurationMethod (string)
Specifies the full name of the method that will be called to build the fluent configuration. For example, My.Assembly.Configuration.ConfigureTypings. The assembly does not need to be specified - Reinforced.Typings itself will find this method either in the assembly of the project itself, or in its references. The method must be static and accept a single parameter of type Reinforce.Typings.Fluent.ConfigurationBuilder (the name of the parameter is not important). It is worth noting that you still have the ability to use attributes, even if you are using a fluent configuration. However, keep in mind that if there is both a fluent and attribute configuration for the same member, then fluent will be preferred.

RtWriteWarningComment (true / false)
Controls writing to the output file of the warning message that the file was generated automatically. If this parameter is set to true, then the header of each generated file will display the title "// This code was generated by a Reinforced.Typings tool." Blah blah blah. To be honest, I do not know who this parameter might be useful, but it is.

RtExportPureTypings (true / false)
Causes RT to generate TypeScript tiping (.d.ts) instead of the usual TypeScript. Enabling this configuration option causes the library to justify its name (although disabled by default). And the thing is that the syntax .d.ts and .ts are slightly different. Hence the need for the existence of this parameter.

RtDivideTypesAmongFiles (true / false)
When this parameter is false, all generated tayping will be written to one file specified in the parameter RtTargetFile. This is not always convenient, because it can lead, for example, to monstrous merdzham when using SCM. If you set this parameter to true, then the generated TypeScript will be scattered across different files (class-per-file). This will ignore the RtTargetFile. Just do not forget to add the generated files to your project by hand as they appear!
You can configure what and where to put using the [TsFile] / fluent-call attribute .ExportTo. It is worth noting that RT itself will understand perfectly with the addition of /// <reference ...> directives to the adjacent types used in this case. But in case of difficulty, you can always help him with the help of the attribute [TsAddTypeReference] / fluent-call .AddReference

RtTargetDirectory (string)
Used in conjunction with RtDivideTypesAmongFiles to specify the directory in which all generated files will be dumped. Be sure to specify this parameter if you are using RtDivideTypesAmongFiles.

RtRootNamespace (string)
It is also used in conjunction with RtDivideTypesAmongFiles. The fact is that through Reflection it is impossible to determine the root namespace of the assembly. And without this, it will not be possible to correctly spread the generated files to directories.

RtBypassTypeScriptCompilation (true / false)
TypeScript is built first when building a project. Sometimes there is a situation that you have generated taipings, and they have made the TypeScript code of your project uncollectible. And in order to fix it, you need to rebuild the project and regenerate the taipings, but you cannot do this, because the project is not going to because the typing of scripts is not going to happen. To get out of this vicious circle allows setting RtBypassTypeScriptCompilation to true. This setting disables the typing of the scripts before building the project and assembles them after assembling the project, which gives the .dll of your project to calmly assemble, and RT to generate fresh taipings from it. Please remember to return this parameter to false when the problem is resolved. Otherwise, there may be problems with publishing the collected javascript.

RtCamelCaseForMethods (true / false) (implemented by Tremor feature request)
Forces the conversion of all method names into camelCase (instead of the traditional .NET PascalCase). Used by aesthetes from javascript. Also camelCase-ing can be controlled separately for each method by using the ShouldBeCamelCased property of the TsFunction attribute.

RtCamelCaseForProperties (true / false) (implemented Tremor feature request)
Same as RtCamelCaseForMethods, just for properties. ShouldBeCamelCased is also found in TsProperty.

RtGenerateDocumentation (true / false)
When set to true, as well as enabling documentation generation in XML in the settings of your project, Reinforced.Typings will pull out the path to the file with XMLDOC during the build process and convert it to jsdoc, which will add to the generated files. In this approach, everything is fine, except that by default, after the XML documentation is included in the export project, the compiler starts filling you up with vorning for undocumented public classes / class members. Not that it somehow interfered technically, but visually infuriates.

RtDisable (true / false)
Disables Reinforced.Typings. As long as this parameter is true, the procedure for generating taipings will not be called. However, RtBypassTypeScriptCompilation will still be active.

Item Group RtAdditionalAssembly
You can drop additional assemblies into this Item-group, which Reinforced.Typings should take into account when exporting (read: export taipings to and from them too). RT has full paths to the References of your project, so you can include in the RtAdditionalAssembly simply the name of the assembly (everything before .dll).


Attributes


One assembly configuration for successful export is not enough. You also have to specify RT what exactly you want to see in the generated TypeScript files. This can be done, for example, by hanging the corresponding attributes over the exported types (classes, interfaces, enums) and their members. This is what I call the attribute configuration. There is also a fluent configuration, but I will talk about it a little later, because it is completely based on the attribute and largely repeats it, and according to this, it will be more pedagogical to tell first about the attribute configuration.
All attributes of Reinforced.Typings are, oddly enough, in the Reinforced.Typings.Attributes namespace, which can confuse a newbie (sarcasm). All attributes can be inherited. All properties are overloaded. As one of the available techniques of work - you can inherit from any of them and make your attribute so as not to drag a pack of parameters with you each time.
Below I give a cheatsheet for all available attributes. The expression “required attribute” in it means that if you do not put this attribute, then the corresponding entity will not be exported to TypeScript.

Attribute NameRequired?Result in taipingExported (patient)
TsInterfaceYesTypeScript interfaceClass, interface, structure
TsClassYesTypeScript classClass structure
TsPropertyNotInterface / class fieldProperty, field (class / structure)
TsFunctionNotInterface / class method (in the case of a class, the body is exported as return null; if the method is non-void and as empty in the case of void)Property, field (class / structure)
TsEnumYesTypeScript enumerationenum obviously
TsValueNotOne of the TypeScript enumeration valuesEnum value, not obvious
TsParameterNotParameter (formal argument) of the TypeScript methodThe parameter of the method, oddly enough
TsGenericNotType parameter of the TypeScript method / class / whateverType parameter
TsIgnoreNotPatient will not be exported to TypeScriptProperty, constructor (!), Field, method, parameter

A little about the export of designers
Separately, I want to mention the attribute TsBaseParam. RT can export classes as well as their constructors. In the case when you inherit classes from each other and explicitly call the ancestor's constructor in the successor's constructor using: base () - no information about this can be obtained via Reflection. Therefore, for the correct export of such cases, you can put the TsBaseParam attribute above the constructor. It has one constructor and it takes as input a string array in which you can write any TypeScript expressions. This will all be written in TypeScript super (...). In practice, I have little idea of ​​the usefulness of this attribute, but it is. There is an opinion that this attribute exists just to show how cool Reinforced.Typings are.

As for the rest, if you enable the export of constructors via the corresponding attribute attribute [TsClass], then they will be exported correctly. That is, no special attributes for designers are provided.


Attribute properties


There are a lot of properties, so I turned their list into a spoiler so as not to litter the article. There, in the list of properties, I specify the name of the property, in parentheses the value of the default property and describe what it controls. This is mostly background information that is duplicated in XMLDOC, in English only. By this - open carefully.

Footcloth text
TsInterface

  • AutoI (true) - whether to automatically put the letter I in front of the patient's name (if there is none)
  • AutoExportMethods (true) - whether to export all patient methods automatically. If not, you will put [TsFunction] on the methods yourself.
  • AutoExportProperties (true) - the same as above, but for propertey and [TsProperty]
  • IncludeNamespace (true) - whether to put the patient in the module when exporting
  • Name (null) - overrides the patient's name
  • Namespace (null) - overrides the patient's namespace

TsClass

  • AutoExportProperties , AutoExportMethods , IncludeNamespace , Name , Namespace - similar to TsInterface properties. A plus:
  • AutoExportFields (true) - the same as AutoExportProperties, but for fields
  • DefaultMethodCodeGenerator (null) - allows you to override the code generator for all exported class methods at once

TsProperty

  • Type (null, type name is substituted) - overrides the patient type name in TypeScript. Like a string. Well, that is, you can write anything at all. It helps when the property type is not displayed in TS (you get any), or you need to make a reference to the non-exported type. For example - on jQuery
  • StrongType (null, type name is substituted) - the same as Type, but you can specify .NET type. Convenient for delegates, for example - wrote StrongType = typeof (Func <int, bool>) and order, no need to steam with tons of parentheses
  • Name (null) - overrides the patient's name
  • ForceNullable (false) - says to force to make the field nullable. Well, that is, field: boolean turn into field?: Boolean
  • ShouldBeCamelCased (false) - whether the property name should be converted to camelCase (the Tremor feature is implemented)

TsFunction

  • Type , StrongType - similar to TsProperty properties, but overrides the return type of the method
  • Name (null) - overrides the patient's name
  • ShouldBeCamelCased (false) - whether the method name should be converted to camelCase (the Tremor feature is implemented)

TsEnum

  • IncludeNamespace , Name , Namespace - similar to TsInterface properties. More parameters have not.

TsValue

  • Name - overrides the patient's name (of a specific enum value, that is)

TsParameter

  • Type , StrongType - similar to TsProperty properties, but override method argument type
  • Name (null) - overrides the patient's name
  • DefaultValue (null) - indicates the default value. To be substituted as parameter: boolean = false , for example. Carefully, here you can shoot yourself in the foot.
  • ShouldBeCamelCased (false) - whether the parameter name should be converted to camelCase (the Tremor feature is implemented)

TsGeneric

  • Type , StrongType - similar to TsProperty properties, but override type parameter type

TsBaseParam

  • Values - an array of strings representing a TypeScript expression. Their contents will be used to generate a call to super (...) when exporting a TypeScript class

TsAddTypeReference

  • RawPath - the path to the file that will be written in the directive /// <reference ...>, which will be added to the file with the exported type
  • Type - type, path to the file containing which will be added in the directive /// <reference ...> to the file with the exported type


It is worth noting that attributes have a small inheritance hierarchy, so some properties are in several attributes and do about the same thing (say, Name, overrides the name of the exported class / interface, but also works for method or property parameters).

Export TypeScript code to multiple files


This feature is enabled when RtDivideTypesAmongFiles is set to true in the build config. By default, with no additional configuration, RT scatters all your classes according to the good old tradition of OO languages ​​- the class is in a separate file, and even put in a subdirectory according to Namespaces (in order not to generate extra directories in this connection, it is recommended to use build configuration parameter RtRootNamespace). There is also one nuance - the studio does not always see that any of the types used in the .ts file lies in the adjacent file and underlines with red how much in vain. To avoid such a situation, add the directive /// <reference path = "..."> to all .ts files. — RT , /, , . . . MasterCard TsAddTypeReference. , , TypeScript- .

  • TsAddTypeReference — ///<reference ...> , . , , Type - Reflection . ( [TsAddTypeReference(typeof(AnotherExportedType))]). RT . ( [TsAddTypeReference("../../jquery.d.ts")]), . , enum-. .
  • TsFile — . RtDivideTypesAmongFiles true. RtTargetDirectory
  • TsReference — , . ( ) ///<reference ...> . - . , . , , jQuery. , , [assembly: TsReference("~/Scripts/typings/jquery.d.ts")]. .




CodeGenerator


TsAttributeBase, — CodeGeneratorType Type. What it is? . — , Reinforced.Typings.Generators.ITsCodeGenerator<>, :

ITsCodeGenerator<> (Settings), auto-property — Generate, :

RT , , enum-, , property — Reinforced.Typings.Generators . , ITsCodeGenerator<>.
, , CodeGeneratorType typeof . , . CodeGeneratorType = typeof(string), IntelliSense - . , fluent- ( .WithCodeGenerator<>), .

..., , Reinforced.Typings.Generators.MethodCodeGenerator, ActionInvokeGenerator, glue-code MVC WebAPI promise. , TypeScript [TsClass(AutoExportMethods = false)], — [TsFunction(CodeGeneratorType = typeof(ActionInvokeGenerator))]. . .


Fluent-


. , , namespace-, , . , , . , 1.0.5 fluent-. 2 :
  1. , Reinforced.Typings.Fluent.ConfigurationBuilder. , :
     using Reinforced.Typings.Fluent; namespace TestProject.App_Start { public class TypingsConfiguration { public static void ConfigureTypings(ConfigurationBuilder builder) { //    fluent- } } } 

  2. fluent-. For example:
     <RtConfigurationMethod>TestProject.App_Start.TypingsConfiguration.ConfigureTypings</RtConfigurationMethod> 


Everything. (: ) . fluent- ( - Tremor , ), . , , , .

, ConfigurationBuilder- ( ), , XMLDOC. fluent-, :
 using Reinforced.Typings.Fluent; namespace TestProject.App_Start { public class TypingsConfiguration { public static void ConfigureTypings(ConfigurationBuilder builder) { builder.ExportAsInterface<ILoginInformation>() .WithPublicProperties() .WithProperty(c => c.Email).Type<int>(); builder.ExportAsInterface<ILoginPage>() .WithPublicProperties() .WithPublicMethods() .WithMethod(c => c.FillIn(Ts.Parameter<ILoginInformation>(o => o.Type<string>()))) .Returns<string>(); builder.ExportAsInterface<ILoginPage>() .WithMethod(c => c.AddOrders(Ts.Parameter<OrdersColelction>())); builder.ExportAsInterfaces( new[] { typeof (ILoginPage), typeof (ILoginInformation), typeof(IOrder) }, c => c.ExportTo("login.ts").WithAllMethods(m => m.CamelCase())); } } } 


, . , .
: TsBaseParam TsGeneric . fluent- . fluent- . — .
: Ts.Parameter TryLookupDocumentationForAssembly.
Ts.Parameter — , fluent- . ,
 builder.ExportAsInterface<MyClass>() .WithMethod(m => m.MyMethod( Ts.Parameter<int>(p => p.OverrideName("apple").Type<string>()) )) .Returns<object>(); 

TypeScript-
 export IMyClass { MyMethod(apple:string):any; } /*    class MyClass { public int MyMethod(int a) { return 0; } } */ 

, .
TryLookupDocumentationForAssembly — ConfigurationBuilder-, RT xml- . , ( , ). , .
. .

Conclusion


, , , - . , github. — .
, - feedback-. — issues github . pull- — , fulltime- .
NuGet- , .

Source: https://habr.com/ru/post/267003/


All Articles