Creating Scaffolds and Generators using Yeoman.
Tags: javascript, node, tutorial, codenewbie
Published: 2021-02-03 03:15:04

A quick introduction about Yeoman: according to the official documentation, it is a helper tool to kickstart new projects, prescribing best practices and tools to help you stay productive.
Yeoman doesn't depend on what technology your Scaffold Project is. Each generator is a standalone tool built by Yeoman.
For your information, there are more than 5000 generators with many kinds of technologies and proposes. You can check the generators available here: [Discovering Generators](https://yeoman.io/generators/).

In this devto article, we will implement a generator to create a dummy NodeJS API and Web App React and talk more about Yeoman generator.

GitHub Repository: https://github.com/ricardoham/generator-scaffold
***

# Installation
Let's start with Yeoman global installation:
```
npm install -g yo
```
`yo` is the Yeoman command line utility allowing the creation of projects utilizing scaffolding templates.
***

Our project is a Generator that users can choose one of these options: Front-End React, Node API, or a FullStack application via CLI(command-line interface).

Create the project folder:
```
$ mkdir generator-scaffold && cd generator-scaffold
```
Run for package.json initial entries.

```
npm init
```
And install the lasted version of Yeoman as a dependency.
```
npm install --save yeoman-generator
```
Add the name of the generator in the "name" property of package.json like bellow:
```json
{
  "name": "generator-scaffold",
  "version": "0.1.0",
  "description": "scaffold example project",
  "files": [
    "generators"
  ],
  "keywords": [
    "yeoman-generator"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ricardoham/generator-scaffold"
  },
  "dependencies": {
    "chalk": "^2.4.2",
    "yeoman-generator": "^3.2.0"
  }
}
```
By convention, the folder must be named `generator-<name_of_genenarator>`. This is important because Yeoman will check if this exists on the file system to find available generators.

Use `files` property as an array, it will be used to set the files and directories of the generator.

Create a sequence of folders:
```
|- generators
|-- app
|--- templates
|---- api
|---- frontend
```

Basically, the generators folder will hold all the generators applications(app) and templates is the scaffolding, in this example, we have api and frontend scaffolds.
***
# Coding the generator
Create **index.js** file in the app folder and then add the main class import yeoman-generator and add our first code lines:
```javascript
module.exports = class extends Generator {

  constructor(args, opts) {
    super(args, opts);
    this.argument('appname', { type: String, required: false });
  }
};
```
Using the **constructor** we can put custom code that will be called first, like in this example, the **appname** is not a required argument when starting the application.
***

One of the main goals of creating a generator is the interaction with the user. To do so we can use **Prompts**. The prompts module is provided by [Inquire.js](https://github.com/SBoudrias/Inquirer.js), a third-party library with a collection of common interactive command line user interfaces(CLI). In general, a prompt method is async and returns a promise. It's totally friendly of async-await from ECMAScript spec.
```javascript
async prompting() {
    this.answers = await this.prompt([{
      type: 'input',
      name: 'name',
      message: 'Your project name',
      default: this.appname, // appname return the default folder name to project
      store: true,
    },
    {
      type: 'list',
      name: 'templateType',
      message: 'Select the template wanted:',
      choices: ['Front-End React', 'Node API builder', 'FullStack Application']
    }]);
  }
``` 
During the run process, the methods will run one by one sequentially on the Yeoman loop. To avoid calling a method by mistake there are three approaches:

1. Use a private method
2. Use instance methods.
3. Extend a parent generator.

So in this case let's create custom **private methods** that won't run on Yeoman loop:
```javascript
// ...
_writingReactTemplate() {
    this.fs.copy(
      this.templatePath('frontend'),
      this.destinationPath('frontend')
    )
    this.fs.copyTpl(
      this.templatePath('frontend/public/index.html'),
      this.destinationPath('frontend/public/index.html'),
      { title: this.answers.name } // Embedded JavaScript templating.
      
    )
  }

  _writingApiTemplate() {
    this.fs.copy(
      this.templatePath('api'),
      this.destinationPath('api')
    )
  }
```
Take a look at these methods: `this.fs.copyTpl` `this.fs.copy`
`this.destinationPath` `this.templatePath`
Yeoman interacts with the file system using some of these methods, here we set a template path and a destination of the copy folder, with `copyTpl` We can Embed the name of the project into **HTML** file template for example in <title /> tag.
```javascript
{ title: this.answers.name } // Embedded JavaScript templating.
```
It uses the [EJS](https://ejs.co/) syntax to do so.
```html
// index.html
<title><%= title %></title>
```
More information: https://yeoman.io/authoring/file-system.html

And we can use the user inputs storage on *this.answers* to handle the scaffold the application will create:
```javascript
// ...
  writing() {
    if (this.answers.templateType === 'Front-End React') {
      this._writingReactTemplate();
    } else if (this.answers.templateType === 'Node API builder') {
      this._writingApiTemplate()
    }
    else {
      this._writingReactTemplate()
      this._writingApiTemplate()
    }
  }
```
Notice there is a method called: **writing()** in the snippet above, Yeoman defines a list of priority methods that means when the loop is running some of the special methods could be called, the higher priority it's **initializing** and the less it's **end**.

### The complete priority methods:
**initializing** - the method's initialization for example the initial state of the project, initial configs, etc.
**prompting** - CLI prompt for options to the user.
**configuring** - To save project configs and save metadata
**default** - Usable when a method doesn't merge with application priority.
**writing** - It's responsible to write the specifics files of a generator for example: template, routes, etc.
**conflicts** - Handler for conflicts(internal use).
**install** - Where the install methods are called(npm, bower, go get).
**end** - Last method to call we can put finish messages, cleanup, etc.

### The code from index.js:
 
```javascript
var Generator = require('yeoman-generator');
var chalk = require('chalk');

module.exports = class extends Generator {

  constructor(args, opts) {
    super(args, opts);
    this.argument('appname', { type: String, required: false });
  }

  // Async Await
  async prompting() {
    this.answers = await this.prompt([{
      type: 'input',
      name: 'name',
      message: 'Your project name',
      default: this.appname, // appname return the default folder name to project
      store: true,
    },
    {
      type: 'list',
      name: 'templateType',
      message: 'Select the template wanted:',
      choices: ['Front-End React', 'Node API builder', 'FullStack Application']
    }]);
  }

  install() {
    this.npmInstall();
  }

  writing() {
    if (this.answers.templateType === 'Front-End React') {
      this._writingReactTemplate();
    } else if (this.answers.templateType === 'Node API builder') {
      this._writingApiTemplate()
    }
    else {
      this._writingReactTemplate()
      this._writingApiTemplate()
    }
  }
  
  _writingReactTemplate() {
    this.fs.copy(
      this.templatePath('frontend'),
      this.destinationPath('frontend')
    )
    this.fs.copyTpl(
      this.templatePath('frontend/public/index.html'),
      this.destinationPath('frontend/public/index.html'),
      { title: this.answers.name } // Embedded JavaScript templating.
      
    )
  }

  _writingApiTemplate() {
    this.fs.copy(
      this.templatePath('api'),
      this.destinationPath('api')
    )
  }

  end() {
    this.log(chalk.green('------------'))
    this.log(chalk.magenta('***---***'))
    this.log(chalk.blue('Jobs is Done!'))
    this.log(chalk.green('------------'))
    this.log(chalk.magenta('***---***'))
  }
};
```
I use **chalk** lib to colorize the prompt and some methods of Yeoman to illustrate the priorities.
***

# Running the Generator
run `npm link` in the root folder of this project
navigate to the directory you want to run the generator ex: **my_new_project_folder** 
run `yo scaffold` and follow the instructions of **CLI**

![CLI](https://dev-to-uploads.s3.amazonaws.com/i/dmuvonby01zapda0y8da.gif)

# Troubleshooting
Got any error?
Try to run `yo doctor` on app root, the yeoman doctor catches errors about what is missing(dependencies, maybe a malfunction method, etc)
You can also use `this.log(something)` or you can debug your generator following this tutorial: https://yeoman.io/authoring/debugging.html
***

That's all folks, I hope you enjoy this tutorial and help you to create your own generators.
Thank you, stay safe! 👋