I am looking to do some TypeScript code generation with CodeSmith and I’d like to use PreserveMerge merging strategy(preserve merge preserves your custom code within generated code between regenerations). CodeSmith’s merging mechanism is powerful and flexible (besides preserve it supports insert merge strategy as well. But if you feel constrained then feel free to create your own merging strategy). Out of the box it supports C#, VB.NET, XML and T-SQL start region and end region notation. This is a problem for TypeScript because, it can’t use any of those with TypeScript files hence I have to define my own start and end region keywords.
So I came up with
/// #region [Name]
and
/// #endregion
notations – TypeScript will ignore them (as comments) and they are explicit enough. Nothing Earth shattering but they should do the job for me.
Definining new region keywords for CodeSmith
Now I have to update CodeSmith with my these new TypeScript region keywords.
CodeSmith uses regular expressions for searching region keywords. They are defined in registry under the key HKEY_CURRENT_USER\Software\CodeSmith\<VERSION>\LanguageRegionDefinition. Here is a C# definition
The definition consists of LanguageKeys, RegionStartRegex and RegionEndRegex. Pretty much self-explanatory.
Here is my TypeScript entry under next available number 4 (also attached to this post):
LanguageKeys: Typescript|TS RegionStartRegex: ^[ \t]*///[ \t]*\#region(?:[ \t]*(?<name>[^\r\n]*))?[ \t]*\r?\n RegionEndRegex: ^[ \t]*///[ \t]*\#endregion[ \t]*\r?\n
Creating the node 4 as a sibling to other region definitions does the trick. Note: LanguageKeys is important when you set the merge strategy (you can use any of strings that are pipe delimited: either TypeScript or TS in my case).
Using regions in TypeScript generated code
Let’s create a very simple proof of concept (the project is attached to this post).
First create new console project and then add a CodeSmith template for TypeScript code. As a TargetLanguage you could put "TypeScript” even though CodeSmith doesn’t really know how to syntax color it. It is just a hint for CodeSmith. Place one or more region delimiters within template (mine is really simple one, just to show the concept), here is my template:
<%@ Template Language="C#" TargetLanguage="TypeScript" Debug="False" %> module RegionTest { function tubo(){ var i = 5; /// #region Test /// #endregion } }
Add CodeSmith project file and add the above template as an output. In the Edit Output settings set the proper output file name (test.ts in my case), Merge type as “PreserveRegions” and Initialization string as “RegionNameRegex=.*;” or “RegionNameRegex=.*;Language=TypeScript;” if you didn’t set it as TargetLanguage (it defaults to template’s TargetLanguage when not defined). RegionNameRegex gives you a chance to use only selected region names for preserve strategy – in my case any name or no name will do. Below are my Edit Output settings.
Generate the code, the output content should appear just like the template definition since there is no real code generation.
Add custom code within the Test region in generated test.ts file (not in template), like:
/// #region Test alert ("wow, this is really a persistent alert"); /// #endregion
And generate the code once more. If everything is in place the new output should have persisted the custom code of the Test region.
Conclusion
To enable CodeSmith’s PreserveRegion merging strategy in an unsuported TypeScript language I had to add a registry key and that’s it. The simplicity of making a language supported in this case shows the CodeSmith flexibility and power and the CodeSmith’s use or registry shows bad practice they should really use files instead of registry (hint).