Creating Project Templates for dotnet – Part 2 – Optional Files

This is part 2 of a 4 part series of posts

In my previous post “Creating Project Templates for dotnet – Part 1 – Getting Started” we created a simple project template that creates an ASP.NET MVC Web application and an XUnit test project. This project contains some of my favorite NuGet packages and has been updated to use libman to manage the static dependencies..

Next let’s take a look at how to support optional files in your project. This might be groups of class files to support a particular type of functionality you and your team use in most projects or something as simple as a read me or license file. For our example I’m going to add in a ReadMe.md, a License.txt, an .editorconfig, and a .gitignore file.

For the read me file I’ll just create a simple markdown file with a heading for my project.

# DotNetNinja.Templates.Mvc

The DotNetNinja.Templates.Mvc in the file will be replaced by the template engine when the file is created leaving me with a ReadMe.md file that has a heading of my solution name.

Next I’ll add an MIT License file as I am planning on using this template mostly for sample projects on GitHub. For that I’m just going to copy the license file from my repository into the content folder.

To get an appropriately configured .gitignore file for .Net projects I’ll use the gitignore template that ships with .Net 5.0.

dotnet new gitignore

And finally to get a basic .editorconfig file I’ll just export my settings from Visual Studio by going to Tools > Options > Text Editor > C# > Code Style > General and clicking the big “Generate .editorconfig file from settings” button.

Once the files are created we need to configure our template to allow a user to include/exclude them when running our template. To do that we need to open up our template.json file in the .template.config folder and add in some more settings. First we’ll add in a block of settings to specify the parameter names for the command line arguments for our template.

   {    
    "$schema": "http://json.schemastore.org/template",
...
    "symbols": {
        "readme": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        },
        "license": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        },
        "gitignore": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        },
        "editorconfig": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        }
    }
...
}

Note that each item has a data type of boolean and that I have chosen to default them to true so that users of our template do not have to specify these parameters and by default they will get all of the files. If they want to skip one pf them (for example the License.txt file) they can add on a parameter –license false.

Now that we have declare these parameters for our template we need to set up our configuration to exclude the files if the parameters are set to false.

{
    "$schema": "http://json.schemastore.org/template",
...
        "sources": [
        {
            "modifiers": [
                {
                    "condition": "(!readme)",
                    "exclude": [
                        "ReadMe.md"
                    ]
                },
                {
                    "condition": "(!license)",
                    "exclude": [
                        "License.txt"
                    ]
                },
                {
                    "condition": "(!gitignore)",
                    "exclude": [
                        ".GitIgnore"
                    ]
                },
                {
                    "condition": "(!editorconfig)",
                    "exclude": [
                        ".editorconfig"
                    ]
                }
            ]
        }
    ]
}

Notice that “exclude” is an array, so if you need to you can exclude multiple files with one condition. The entire completed file should now look something like this:

{    
    "$schema": "http://json.schemastore.org/template",
    "author": "Larry House",
    "classifications": [ "Web" ],
    "name": "DotNetNinja MVC Template",
    "identity": "DotNetNinja.Templates.Mvc",        
    "shortName": "ninjamvc",                 
    "tags": {
        "language": "C#",    
        "type": "project"                 
    },
    "sourceName": "DotNetNinja.Templates.Mvc",
    "preferNameDirectory" : true,
    "symbols": {
        "readme": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        },
        "license": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        },
        "gitignore": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        },
        "editorconfig": {
            "type": "parameter",
            "datatype":"bool",
            "defaultValue": "true"
        }
    },
    "sources": [
        {
            "modifiers": [
                {
                    "condition": "(!readme)",
                    "exclude": [
                        "ReadMe.md"
                    ]
                },
                {
                    "condition": "(!license)",
                    "exclude": [
                        "License.txt"
                    ]
                },
                {
                    "condition": "(!gitignore)",
                    "exclude": [
                        ".GitIgnore"
                    ]
                },
                {
                    "condition": "(!editorconfig)",
                    "exclude": [
                        ".editorconfig"
                    ]
                }
            ]
        }
    ]
}

Now if we reinstall our template and test it out we can see that our files are included and that if we set one of the parameters to false that the corresponding file is not output into our new solution. I’ve added a PowerShell script to the root of the repository to make this easier.

$shortName = "ninjamvc"
$id = "DotNetNinja.Templates.Mvc"

# Check if the template is already installed
$templates = dotnet new -l
$template = $templates | Select-String $shortName
# If installed then uninstall it
if($null -ne $template){
    Write-Host "Removing $id"
    dotnet new -u $id
}
# Clear Template Cache
Write-Host "Clearing Template Cache"
Remove-Item ~/.templateengine -Recurse -Force

#Build new Package
Write-Host "Building Template Package $id"
.\Build-Template.ps1

# Install Template
Write-Host "Installing new Template $id"
dotnet new -i $id

This script will uninstall the template if it is already installed, clear out the template cache (this will be especially important later when we start working with Visual Studio, build our template, and install it.) With that in place all we need to do is run the script.

.\Test-Template.ps1

Let’s start testing our updated template by generating solution with all files:

dotnet new ninjamvc -n SampleWeb

Output:

Now we can try generating a solution without read me file:

dotnet new ninjamvc --Readme false -n TestWeb

Output:

In my next post we will see how to add optional blocks of content in our files and tie that together with optional files to implement an optional feature in our template.

Resources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.