Creating a yeoman generator

In the previous post we looked at the basics of getting started with yeoman and the tools etc. around it. Let’s now write some real code. This is going to be a generator for creating a node server using my preferred stack of technologies and allow the person running it to supply data and/or options for the generator to allow it to generate code specific to the user’s needs.

Input

Our node server will offer the option of handling REST, websocket or GraphQL endpoints. So we’re going to need some input from the user to choose the options they want.

First off, DO NOT USE the standard console.log etc. methods for output. Yeoman supplies the log function for this purpose.

Here’s an example of our generator with some basic interaction

var Generator = require("yeoman-generator");
module.exports = class extends Generator {
    async prompting() {
        const input = await this.prompt([
            {
                type: "input",
                name: "name",
                message: "Enter project name",
                default: this.appname
            },
            {
                type: "list",
                name: "endpoint",
                message: "Endpoint type?",
                    choices: ["REST", "REST/websocket", "GraphQL"]
            }
        ]);


        this.log("Project name: ", input.name);
        this.log("Endpoint: ", input.endpoint);
    }
};

Now if we run our generator we’ll be promoted (from the CLI) for a project name and for the selected endpoint type. The results of these prompts will then be output to the output stream.

As you can see from this simple example we can now start to build up a list of options for our generator to use when we generate our code.

Command line arguments

In some cases we might want to allow the user to supply arguments from the command line, i.e. not be prompted for them. To achieve this we add a constructor, like this

constructor(args, opts) {
   super(args, opts);

   this.argument("name", { type: String, required: false });

   this.log(this.options.name);
}

Here’s we’ve declared an argument name which is not required on the command line, this also allows us to run yo server –help to see a list of options available for our generator.

The only problem with the above code is that if the user supplies this argument, they are still prompted for it via the prompting method. To solve this we can add the following

yarn add yeoman-option-or-prompt

Now change our code to require yeoman-option-or-prompt, i.e.

var OptionOrPrompt = require('yeoman-option-or-prompt');

Next change the constructor slightly, to this

constructor(args, opts) {
   super(args, opts);

   this.argument("name", { type: String, required: false });

   this.optionOrPrompt = OptionOrPrompt;
}

and finally let’s change our prompting method to

async prompting() {

   const input = await this.optionOrPrompt([           
      {
         type: "input",
         name: "name",
         message: "Enter project name",
         default: this.appname
      },
      {
         type: "list",
         name: "endpoint",
         message: "Endpoint type?",
            choices: ["REST", "REST/websocket", "GraphQL"]
      }
   ]);

   this.log("Project name: ", input.name);
   this.log("Endpoint: ", input.endpoint);
}

Now when we run yo server without an argument we still get the project name prompt, but when we supply the argument, i.e. yo server MyProject then the project name prompt no longer appears.

Templates

With projects such as the one we’re developing here, it would be a pain if all output had to be written via code. Luckily yeoman includes a template capability from https://ejs.co/.

So in this example add a templates folder to generators/app and then within it add package.json, here’s my file

{
    "name": "<%= name %>",
    "version": "1.0.0",
    "description": "",
    "module": "es6",
    "dependencies": {
    },
    "devDependencies": {
    }
  }

Notice the use of <%= %> to define our template variables. The variable name now needs to be supplied via our generator. We need to make a couple of changes from our original source, the const input needs to change to this.input to allow the input variable to be accessible in another method, the writing method, which looks like this

writing() {
   this.fs.copyTpl(
      this.templatePath('package.json'),
      this.destinationPath('public/package.json'),
         { name: this.input.name } 
   );
}

here’s the changed prompting method as well

async prompting() {

   this.input = await this.optionOrPrompt([           
      {
         type: "input",
         name: "name",
         message: "Enter project name",
         default: this.options.name
      },
      {
         type: "list",
         name: "endpoint",
         message: "Endpoint type?",
            choices: ["REST", "REST/websocket", "GraphQL"]
      }
   ]);
}

Now we can take this further

{
    "name": "<%= name %>",
    "version": "1.0.0",
    "description": "",
    "module": "es6",
    "dependencies": { <% for (let i = 0; i < dependencies.length; i++) {%>
      "<%= dependencies[i].name%>": "<%= dependencies[i].version%>"<% if(i < dependencies.length - 1) {%>,<%}-%>
      <%}%>
    },
    "devDependencies": {
    }
  }

and here’s the changes to the writing function

writing() {

   const dependencies = [
      { name: "express", version: "^4.17.1" },
      { name: "body-parser", version: "^1.19.0" }
   ]

   this.fs.copyTpl(
      this.templatePath('package.json'),
      this.destinationPath('public/package.json'),
      { 
         name: this.input.name, 
         dependencies: dependencies 
      } 
   );
}

The above is quite convoluted, luckily yeoman includes functionality just for such things, using JSON objects

const pkgJson = {
   dependencies: {
      "express": "^4.17.1",
      "body-parser": "^1.19.0"
   }
}

this.fs.extendJSON(
   this.destinationPath('public/package.json'), pkgJson)

Storybook render decorator

Storybook have a bunch of really useful decorators.

This one is useful to see when changes, either data, state or properties cause re-rendering of our React UI. In some cases it may be that a change should not cause a subcomponent to render, with this decorator we can see what caused the render to occur (or at least help point us towards possible reasons).

We need to install the add-on, like this

yarn add -D storybook-addon-react-renders

and we just add code like this

import { withRenders } from "storybook-addon-react-renders";

storiesOf("Some Component Test", module)
  .addDecorator(withRenders)

Creating scaffolding with yeoman

Yeoman is basically a tool for generating scaffolding, i.e. predefined projects (see generators for a list of some existing generators).

You could generate things other than code/projects equally well using yeoman.

There’s a fair few existing generators but you can also define your own generators and hence… Say for example you have a standard set of tools used to create your Node based servers, i.e. you want Typescript, express, eslint, jest etc. we could use yeoman to set everything up.

Ofcourse you could create a shell script for this or a custom CLI, but yeoman gives you the ability to do all this in Javascipt with the power that comes from that language and it’s eco-system.

Within a yeoman script, we can also interact with the user via the console, i.e. ask for input. Create templates for building code, configuration etc.

Installing the tooling

Before we start using yeoman we need to install it, so run either npm or yarn as shown below

npm install -g yo 

To check everything worked simply run

yo --version

At the time of writing, mine version is 3.1.1.

Before we get onto the real topic of this post, lets just check out some yeoman commands

  • Listing installed generators
    yo --generators
    
  • Diagnose yeoman issues
    yo doctor
    

Creating our own generators

Okay so this is what we’re really interested in. I have a bunch of technologies I often use (my usual stack of tech./packages). For example, if I’m creating a Node based server, I’ll tend to use Typescript, express, jest and so on. Whilst we can, ofcourse, create things like a git repos with everything set-up and just clone it or write shell scripts to run our commands. As mentioned, with yeoman we can also template our code as well as interact with the user via the CLI to conditionally generate parts of our application.

There appears to be a generator for producing generators, but this failed to work for me, but for completeness here it is

npm install -g yo generator-generator

Now, let’s write our first generator…

Run the following, to create our package.json file

yarn init -y

The first thing to note is, the generator name should be prefixed with generator-. Therefore we need to change our “name” within package.json, for example

"name": "generator-server"

The layout of our files is expected to be a either (off of our root)

packages.json
generators/app/index.js
generators/router/index.js

OR

packages.json
app/index.js
router/index.js

Whichever layout we choose should be reflected in package.json like this

"files": [
    "generators"
  ],

OR

"files": [
    "app",
    "router"
  ],

You might, at this point, wonder what the point of the router is, and whilst this is within the yeoman getting started guide, it appears ultimately any folder added alongside the app folder will appear as a “subcommand” (if you like) of your generator. In this example, assuming the app name is generator-server (see below) then will will also see that router can be run using yo server:router syntax. Hence you can create multiple commands under your main yeoman application.

We’ll also need to add the yeoman-generator package before we go too much further, so run

yarn add yeoman-generator

So here’s a minimal example of what your package.json might look like

{
  "name": "generator-server",
  "version": "0.1.0",
  "description": "",
  "files": [
    "generators"
  ],
  "keywords": ["yeoman-generator"],
  "dependencies": {
    "yeoman-generator": "^1.0.0"
  }
}

Writing our generator code

In the previous section we got everything in place to allow our generator to be recognised by yeoman, so let’s now write some code.

Here’s an example of a starting point from the yeoman website.

In generator/app/index.js we have a simple example

var Generator = require("yeoman-generator");
module.exports = class extends Generator {
   method1() {
      this.log('method 1 just ran');
   }
   method2() {
      this.log('method 2 just ran');
   }
};

Sadly this is not using ES6 syntax, maybe I’ll look into that in a future post, but for now it’s not too big of a deal. There is a @types/yeoman-generator package if you want to work with Typescript, but I’ll again leave that for another possible post.

When we get to run this generator, you’ll find that both methods are run hence we get the following output

method 1 just ran
method 2 just ran

All the methods we add to the Generator class are public as so are run by yeoman. We can make them private by prefixing with the method name with an underscore (fairly standard Javascript style to suggest a field or method to be private or ignored).

The order that the methods appear is the order they’re executed in, hence switching these two methods around will result in method2 running first, followed by method1.

We’re not going to write any further code at this point, I’ll leave coding the generator for another post.

Testing our generator

At this point we don’t want to deploy our generator remotely, but want to simply test it locally. To do this we run the following command from the root folder of our generator

yarn link

This will create a symbolic link for npm/yarn and now we can run

yo --generators

which should list our new generator, named server.

Now we have our generator available to yeoman, we simply type

yo server

Obviously server is replaced by the name of your generator.

Haskell basics – Functions

Note: I’ve writing these Haskell blog posts whilst learning Haskell, so do not take them as expert guidance. I will amend the post(s) as I learn more.

Functions are created using the format

function-name params = function-definition

Note: function names must be camelCase.

So for example, let’s assume we have a Calculator module with the functions, add, subtract, multiply and divide might look like this

module Modules.Calculator where

add a b = a + b
subtract a b = a - b
multiply a b = a * b
divide a b = a / b

Function can be created without type information (as shown above) however it’s considered good practise to specify type annotations for the functions, so for example let’s annotate the add function to say it takes Integer inputs and returns an Integer result

add :: Int -> Int -> Int
add a b = a + b

Now if we try to use floating point numbers with the add function, we’ll get a compile time error. Obviously its more likely we’d want to handle floating point numbers with this function, so let’s change it to

add :: Double -> Double -> Double

Migrating a folder from one git repo to another

I had a situation where I had a git repo. consisting of a Java project and a C# project (a small monorepo), we decided that permissions for each project needed to differ (i.e. the admin of those projects) and maybe more importantly in a way, changes to one were causing “Pending” changes to the other within CI/CD, in this case TeamCity.

So we need to split the project. Ofcourse it’s easy to create a new project and copy the code, but we wanted to keep the commit history etc.

What I’m going to list below are the steps that worked for me, but I owe a lot to this post Move files from one repository to another, preserving git history.

Use case

To reiterate, we have a Java library and C# library sitting in the same git code base and we want to move the C# library into it’s own repository whilst keeping the commit history.

Steps

  • Clone the repository (i.e. the code we’re wanting to move)
  • CD into it
  • From a command line, run
    git remote rm origin
    

    This will remove the remote url and means we’re not going to accidently commit to the original/source repository.

  • Now we want to filter our anything that’s not part of the code we want to keep. It’s hoped that the C# code, like ours, was in it’s own folder (otherwise things will be much more complicated). So run
    git filter-branch --subdirectory-filter <directory> -- --all
    

    Replace with the relative folder, i.e. subfolder1/subfolder2/FOLDER_TO_KEEP

  • Run the following commands

    git reset --hard
    git gc --aggressive 
    git prune
    git clean -fd
    
  • Now, if you haven’t already create a remote repository, do so and then run
     
    git remote add origin <YOUR REMOTE REPO>
    
  • // this should have been handled by step 6 git remote set-url origin https://youreposerver/yourepo.git

  • git push -u origin --all
    git push origin --tags
    

Getting started with jekyll

GitHub pages, by default, uses jekyll and I wanted to get something running locally to test things.

Getting everything up and running

Let’s start by installed Ruby by going to RubyInstaller for Windows Downloads if you don’t already have Ruby and Gem installed.

Now go through the Jekyll Quick-start Instructions – I’ll list them here also.

  • gem install bundler jekyll
  • jekyll new my-awesome-site
  • cd my-awesome-site
  • bundle exec jekyll serve

So if all went well, the last line of these instructions will run up our jekyll site.

Testing our GitHub pages

  • Clone (if you don’t already have it locally) you repository with you GitHub pages
  • Run git checkout master, i.e. or where you store your markdown/html file content (in other words not the gh-pages branch if you’re using the standard master/gh-pages branches).
  • I don’t have a Gemfile, so in the root folder, create a file name Gemfile and here’s the contents (if you have a Gemfile add these two lines)
    source 'https://rubygems.org'
    gem 'github-pages', group: :jekyll_plugins
    
  • Run bundle install
  • Run bundle exec jekyll serve

Note: You can commit the Gemfile and Gemfile.lock files to your GitHub repository, these are not used by GitHub Pages.

After you’ve run up the server a _site folder will be created, these need not be committed.

Changing the theme

The first thing you might want to try is change the theme to one of the other supported themes. Simply open the _config.yml file and change the name of the theme, i.e.

theme: jekyll-theme-cayman

Other supported themes include

If you change the theme you’ll need to shut the server down and bundle exec jekyll serve which will run jekyll build and update the _site directory.

Dependency Injection and Blazor

In a previous post Blazor routing and Navigation we injected the NavigationManager into out page using the following

@inject NavigationManager NavManager

So when we use @inject followed by the type we want injected, ASP.NET/Blazor will automatically supply the NavigationManager (assuming one exists).

Adding services

Ofcourse we can also add our own types/services to the DI container.

On a Blazor WebAssembly application, we can add types to the Program.cs, Main method, for example

public static async Task Main(string[] args)
{
   var builder = WebAssemblyHostBuilder.CreateDefault(args);
   // template generated code here

   // my custom DataService
   builder.Services.AddSingleton<IDataService, DataService>();

   await builder.Build().RunAsync();
}

In Blazor Server, we add our types to the Startup.cs, ConfigureServices method, for example

public void ConfigureServices(IServiceCollection services)
{
   // template generated code here

   // my custom DataService
   services.AddSingleton<IDataService, DataService>();
}

Service lifetime

In the examples in the previous section we added the service as a singleton.

  • Scoped – this is means the service is scoped to the connection. This is the preferred way to handle per user services – there is no concept of scope services in WebAssembly as obviously it’s a client technology at this point and already per user scoped
  • Singleton – As you’d expect, this is a single instance for the lifetime of the application
  • Transient – each request for a transient service will receive a new instance of the service
  • If you need access to service is a Component class, i.e. you’re creating your own IComponent you have mark a property with the InjectAttribute

    public class MyService
    {
       [Inject]
       IDataService DataService { get; set; }
    }
    

    Ofcourse constructor injection (my preferred way to do things) is also available, so we just write code such as this, assuming that MyService is created using the service container

    public class MyService
    {
    public MyService(IDataService dataService)
    {
    // do something with dataService
    }
    }

    Destructing in JavaScript

    In JavaScript/TypeScript, if you’re using the Prefer destructuring from arrays and objects (prefer-destructuring) eslint rule, you’ll want to use destructing syntax to get values from objects and arrays.

    If we imagine we have an object like this

    class Person
    {
       firstName: string;
       lastName: string;
    }
    

    The to get the firstName from a Person instance, we tend to use

    const firstName = person.firstName;
    

    Instead this rule prefers that we use the following syntax

    const { firstName } = person;
    

    If for some reason (for example in React if you’re destructing state which may have been changed) you have need to get the value using destructing syntax but assigned to a new variable/value name, then we use

    const { firstName: fname } = person;
    

    Configuration and Blazor

    In a previous post we looked at dependency injection within Blazor. One of the services available by default is an implementation of IConfiguration, hence to inject the configuration object we do the following

    @inject IConfiguration Configuration
    

    and we can interact with the configuration using a key, i.e.

    @Configuration["SomeKey"]
    

    For Blazor on the server we can simply add the key/value to the appsettings.json file, like this

    "SomeKey":  "Hello World" 
    

    By default Blazor WebAssembly does not come with an appsettings.json, so you’ll need to add one yourself. This file will need to be in the wwwroot folder as these files will be deployed at part of your client.

    You can put sensitive data in the server appsettings.json because it’s hosted on the server, do not put sensitive information in the appsettings.json for a WebAssembly/client otherwise this will be downloaded to the user’s machine.

    If you want to store more complicated data in the appsettings.json, for example a JSON object, you’ll need to create a class that looks like the data, for example if your appsettings.json had the following

    "Person": {
      "FirstName": "Scooby",
      "LastName": "Doo"
    } 
    

    So now we’ll need a class to match the structure of the data. The name of the class is not important, the structure is, so here we have

    public class MyPerson
    {
       public string FirstName { get; set; }
       public string LastName { get; set; }
    }
    

    Now to access this we need to use

    Configuration.GetSection("Person").Get<MyPerson>();
    

    References

    Carl Franklin’s Blazor Train: Configuration and Dependency Injection

    Adding images to your github README…

    Obviously there are several ways to add images to your GitHub README.md or other mark down files in GitHub.

    1. Check them into the repository that includes the README.md
    2. Use a CDN or other file storage outside of GitHub
    3. Attach the image to an issue within your repository

    If you are going to use the first option, then there’s an obvious downside, in that the image(s) are now part of your repo. and will add to the overall download, clone, fork etc.

    Obviously the second option is great if you have CDN access of other filer space.

    The third option is presented on GitHub Tricks: Upload Images & Live Demos and simply requires

    1. Create an issue in your repo
    2. Drag and drop the image from File Explorer (or the likes) into the comment section of the issue
    3. When the link is updated in the comment, i.e. you see the image, copy the link using your browser (i.e. right mouse click, copy link)
    4. Now use this link in your markdown

    To use the link in GitHub markdown just use

    ![You Text](Image Url)