Insights

Leprechaun: A Typescript Model Generator for Sitecore JSS

A detailed explanation about Leprechaun and how to use the tool to generate TypeScript models

What is Leprechaun?

Created by Ben Lipson, Leprechaun is a code generation tool which uses the RoslynCode Generator. This means, it uses the serialized yml files to generate the code on the go. Hence, eliminating the need of using Visual Studio.

Why Use Leprechaun?

In the earlier versions of Sitecore (9.x), the “All In One” or also known as the “Monolithic” architecture was widely used. Because of this, The front end and the back end code could use the same C# models or objects. Glass Mapper is one of the popular ORMs which was used in most of the projects.

But, As we move towards the headless architecture in Sitecore (10.x), the burden of handling complex operations lie upon the front end code. To reduce this burden the least we can do is use an ORM which would help us create models useful for both front end and back end code without causing any conflicts.

Here’s where Leprechaun steps in. With the use of leprechaun config file, we can create C# as well as Type Script models. The best part about this is it uses serialized .yml files to generate the models. So, the field names would remain the same, resulting in no conflicts.

Moreover, It can be run via an executable added in the pre-build in MSBuild or you can create a Command line / PowerShell script to run with your CI/CD or If you want to run the tool for your node applications, you can create a gulp task.

Advantages of Using Leprechaun

  • It can generate models for JSS Typescript, Habitat, Synthesis, and Glass.
  • Users can create custom C# script files (.csx) to generate custom models.
  • All the scripts (.csx) are easy to read and modify.
  • No merge conflicts, as the models are generated run time on the servers.
  • It is Helix friendly as most of the code is config based.
  • It also has a Watch functionality which keeps an eye on the changes in .yml file and auto generate the models based on the change. However, This functionality is currently not supported for Sitecore Serialization.

Deep Dive into the Leprechaun Configuration

Everything that Leprechaun is, or offers, depends on this config. This config is majorly divided into three settings. The default settings (<default> node), shared settings (<shared> node) and custom settings (<configurations> node). The config also deals with a few variables which will be replaced by user inputs. Lets take a brief look at all the important aspects of the config.

Variables

First of, We will see all the variables used in the config and their significance. There are 3 variables used in the config. $(layer), $(module) and $(configDirectory). $(configurationName) is also a variable mentioned but is not used.

$(layer) and $(module) are concepts used from Helix principle. Here, layers are the folder structure we create to segregate modules such as Foundation, Feature, Project etc. And modules refer to any item present under a specific layer.

For instance, Assume you have a template name “HeroBanner” created under the “Feature” folder in Sitecore (/sitecore/templates/Feature/HeroBanner). This means the $(layer) would be “Feature” and $(module) would be “HeroBanner”.

And finally, The $(configDirectory) is the path to the project directory where you will copy the leprechaun config.

Configurations Node

The major building block of Leprechaun is this <configurations import="**\*.module.json"> node. The “import” attribute present of the node determines the path from where it will import the “module.json” files. This could be different depending upon the paths of your Leprechaun.config and all of your *.module.json files.

Screenshot of a directory showing the location of JSON module files including content.feature.module.json and renderinghost.module.json.

Under the <configurations> node we have a <configuration name="LeprechaunDemo.Base" abstract="true”> node. The “name” attribute defines the name of the configuration, We will discuss this in brief later on. The “abstract” attribute is a boolean, Which is used to override the <default> configurations wherever required.

The <configuration> node has two child nodes, <codeGenerator scripts="Scripts/Synthesis.csx, Scripts/Habitat.csx, Scripts/Diagnostics.csx" outputFile="$(configDirectory)\models\$(layer).$(module).Model.cs" /> and <templatePredicate rootNamespace="$(layer).$(module)" /> and <templatePredicate rootNamespace="Sample.$(layer).$(module)" />.

<codeGenerator> node present under the <default> node defines two more attributes, “type” and “singleInstance”. We don’t need to add these two attributes in the <configuration>/<codeGenerator> node. This is because we have used the “abstract = true” attribute on the <configuration> node. Same goes for all the other nodes and attributes present under <default> node.

The <codeGenerator> node has the “scripts” attribute which is used to determine the type of script needed to generate the models. The “outputFile” attribute determines the path where the generated models will be kept. It requires an absolute path including the file extension.

Finally, we have the <templatePredicate rootNamespace="Sample.$(layer).$(module)" />. The “rootNamespace” attribute defines the name space of the generated model. It also uses the same layer.module concept of Helix. Replace the “Sample” in the namespace with a appropriate name.

Shared Node

As the name suggests, it consists of all the settings which are shared across all configurations. Using the <metaDataGenerator> node, we can override any field type needed. We can also use the <architectureValidator> to disable the validators such as “allowFieldNamesIdenticalToTemplateName” and “allowNovelFieldNames”. Until and unless this is necessary, we should avoid disabling the validators.

Default Node

This node consists of all the default nodes and attributes, which can be overridden in custom settings. The <codeGenerator> node as discussed earlier uses the Roslyn code generator to generate models, Hence it requires the “type” attribute to pass the name space and code generation file path.

The <templatePredicate> node has a <include name=”templates”> node which is used to include the path of templates needed for model generation. The “name” attribute must match with the includes.name found in .module.json file.

Close-up of a JSON configuration showing the template name and path settings.

Similarly, If you want to exclude any template from model generation the <excludedBaseTemplate id="{8CA06D6A-B353-44E8-BC31-B528C7306971}" name="Rendering Parameters Template" /> node under <templateReader> node will do the trick for you. We need to pass the template id in the “id” attribute and template name in the “name” attribute.

Lastly, We have a <fieldFilter> node whose child node <exclude name=”__*”> is used to exclude any fields from generated model. The “name” attribute either takes fieldName or fieldId as parameter.

How to Setup Leprechaun

Before we begin, make sure you have a copy of the Leprechaun Repo on your system. For the setup, we will be using the XM Cloud Starter Kit.

  1. Lets Begin. Open your PowerShell window and traverse to your project directory. This is the folder where you will see all the .config, .sitecore, src folders. We can install it locally to a project or globally. Here, we will install it locally.

    Screenshot showing the JSON file path in a configuration setup, emphasizing 'path' and 'templates'.

    Before that, Make sure you have the “dotnet-tools.json” file present in the “.config” folder. If you don’t have it, type in the below command.

     # Generate the dotnet-tools.json file
    
     dotnet new tool-manifest
    

    Once the initial steps are completed run the below command to install leprechaun locally to your project.

     # Install locally to a project
    
     dotnet tool install --no-cache Leprechaun.Cli
    

    You can install it globally using the below command but for this blog it is recommended to do it locally.

     # Install globally
    
     dotnet tool update --global --no-cache Leprechaun.Cli
    

    After the installation is completed you will see a message similar to below image.

    Terminal output displaying the installation steps for Leprechaun CLI tool with version details.

    You can verify the installation by opening the “dotnet-tools.json” file. Refer the image below.

    JSON configuration snippet showing the correct 'authority' setup for Sitecore.

  2. Next, From the Leprechaun repo, Copy the Leprechaun.config file and paste it in your project directory. It is the same path which was used to install Leprechaun.

    📝 Note : Its better to keep the config file at the root level of the project i.e. where your Sitecore.config resides, as it will be easier to access the yml files for code generation.

    We will need to make some modifications in the config as per our requirements. Lets start with updating the configuration name. By default, it is named as “Sample.Base” but you can name it anything relevant. This configuration name should be same as the “@extends” of “leprechaun” node property in all your *.module.json files. Refer the image’s below.

    Screenshot showing XML configuration snippet defining a name attribute for Leprechaun models.

    Screenshot of a JSON file displaying detailed configuration for a content module including 'items' and 'leprechaun' settings.

    I have renamed my configuration name as “LeprechaunModels.Base” which matches the “@extends” property. Recommendation is to change it to “[SolutionName].Base”.

  3. Next step, We need to generate Type Script classes / models for our front-end code. In the Leprechaun Repo, Navigate to the scripts folder and copy the JssTypeScript.csx file and paste it your project directory.

    I have created a folder in the root directory named “Leprechaun.CodeGen” and have pasted the script inside the folder.

    In the Leprechaun.config file, add the path of the copied script in the <codeGenerator> “scripts” attribute. My path looks something like “$(configDirectory)/Leprechaun.CodeGen/JssTypeScript.csx” . Where, the $(configDirectory) is the path of leprechaun.config.

    Now, We need to add the path “outputFile” attribute where the TypeScript model file will be generated. For this, give the path of your front end repository. As I am using the starter kit, My path turns out like “$(configDirectory)\src\sxastarter\models\$(layer).$(module).Model.ts”. I created an extra folder and named it as “models” for the sake of segregation. Also do not forget the file extension at the end of the path.

  4. Update all the module.json files. Right now, I have created one module.json file. Lets add the Leprechaun code useful to generate the models.

         "leprechaun": {
             "configuration": {
                 "@extends": "LeprechaunModels.Base",
                 "@name": "Feature.ContentFolder"
             }        
         }
    

    As discussed earlier, the “@extends” property is the configuration name attribute in the config. The “@name” requires the path of the template present in Sitecore. It follows the same Layer.Module concept. Refer below image for clarification.

    Detailed view of a JSON file focusing on the configuration path attributes within a module.

  5. Final step, run the below leprechaun command to generate the models.

         dotnet leprechaun -c "C:\XMCloud-Training\Leprechaun.config"
    

    After the command is successfully run, you will see a success message. And your models will be generated.

    Terminal view of Leprechaun generating models with complete process details.

  6. Let’s verify the models generated.

    Terminal output highlighting the path where TypeScript models are generated from templates.

    We can see the TypeScript file is generated. Below is my “content.feature.module.json”

    Detailed JSON configuration for a module including 'name', 'path', and 'commands'. Below is the TS file generated based on the json above.

     /**
      * <auto-generated>
      *     This code was generated by a tool.
      *
      *     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
      * </auto-generated>
      */
    
     // @ts-ignore 
     import { Field, ImageField, FileField, LinkField, Item } from '@sitecore-jss/sitecore-jss-nextjs';
     // @ts-ignore 
     import { content as contentFeaturecontent } from "./content.Feature.model"
    
     export namespace content.Feature.ContentFolder {
    
         /**
         * Represents the template /sitecore/templates/Feature/ContentFolder/DemoContentBlock
         */
         export type DemoContentBlock =  {
             fields?: { 
                 /**
                 * Represents the cotent field (7b5cd158-bbb2-4866-b01a-30addda37097).
                 */
                 cotent?: Field<string>;
    
                 /**
                 * Represents the heading field (75b81c37-749c-4da5-a3ef-d1d5d298b0bb).
                 */
                 heading?: Field<string>;
    
                 /**
                 * Represents the subHeading field (7a17d0d7-2e28-4b1a-abd2-2d55a4734539).
                 */
                 subHeading?: Field<string>;
             }
    
         }
    
         /**
         * Represents the GraphQL template /sitecore/templates/Feature/ContentFolder/DemoContentBlock
         */
         export type DemoContentBlockJson =  {
    
                 /**
                 * Represents the cotent field (7b5cd158-bbb2-4866-b01a-30addda37097).
                 */
                 cotent?: {
                     jsonValue: Field<string>
                 };
    
                 /**
                 * Represents the heading field (75b81c37-749c-4da5-a3ef-d1d5d298b0bb).
                 */
                 heading?: {
                     jsonValue: Field<string>
                 };
    
                 /**
                 * Represents the subHeading field (7a17d0d7-2e28-4b1a-abd2-2d55a4734539).
                 */
                 subHeading?: {
                     jsonValue: Field<string>
                 };
    
         }
     }
    

Tackling the Challenges

I will discuss some of the challenges I faced during the setup and how I resolved those.

  1. Missing type attribute for dependency “name”. This was a silly issue I encountered. It occurred because of a typo I made in the “content.feature.module.json” where I missed “@” sign before the “name” property in leprechaun.configuration object. The detailed error is shown below in the image.

    Terminal output of Leprechaun showing a system exception related to a missing type attribute in a dependency.

  2. Sitecore CLI failed validation. By reading the whole error I understood that the “authority” property was missing in the .sitecore/user.json. When I opened the file I saw there are 2 endpoints. One is xmcloud and the other one is default. I had the “authority” property set in the xmcloud node but it was missing from the default node. So I added the property and the error was resolved. Refer to images below.

    Error message in Leprechaun indicating a missing authority endpoint for XM Cloud.

    JSON snippet highlighting the 'authority' property set to the XM Cloud endpoint.

  3. Missing path attribute. In the leprechaun.config line no 104, which has <include name="Templates" path="/sitecore/templates/$(layer)/$(module)" /> node. The “path” attribute needs to be added during the customization. After adding the above path it started working. Refer below image for full error.

    Terminal output showing an unhandled exception due to a missing 'path' attribute in an XML configuration.

Summing Up the Leprechaun Experience

The purpose of this blog is to enlighten its readers about the power and advantages of Leprechaun. In this Headless era, Where most of the complex problems are front end dependent, Leprechaun could prove to be a time saver by helping its users generate models based on Sitecore templates hassle free. It also eliminates the issue of merge conflicts while serializing Sitecore items, One less thing for the developers to worry about.



Meet Arsalaan Pathan

Sitecore Developer

⚽🎮🛣️

Arsalaan is not just a web developer; he's a certified expert in Sitecore and Content Hub development, bringing a unique blend of technical prowess and creative vision to every project. Since diving into the world of web development in 2016, he has continually honed his skills, initially on .NET platforms before transitioning seamlessly to Sitecore in 2020. With a passion for crafting dynamic and engaging digital experiences, Arsalaan is driven by the challenge of pushing boundaries and delivering innovative solutions that captivate audiences and drive results.

In his free time, he often savors the thrill of spontaneous road trips, exploring new destinations and soaking in the sights along the way. Alternatively, you'll find him on the soccer field, eagerly chasing after the ball, his passion for the game evident in the beads of sweat glistening on his brow. Whether traversing open roads or dominating the field, he embraces each moment with gusto and determination.

Connect with Arsalaan