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.
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.
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.
-
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.
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.
You can verify the installation by opening the “dotnet-tools.json” file. Refer the image below.
-
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.I have renamed my configuration name as “LeprechaunModels.Base” which matches the
“@extends”
property. Recommendation is to change it to “[SolutionName].Base”. -
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. -
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. -
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.
-
Let’s verify the models generated.
We can see the TypeScript file is generated. Below is my “content.feature.module.json”
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.
-
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. -
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 isxmcloud
and the other one isdefault
. I had the“authority”
property set in thexmcloud
node but it was missing from thedefault
node. So I added the property and the error was resolved. Refer to images below. -
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.
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.