What is consultant?



Consultant is a simple, straightforward, language-independent project kickstarter toolkit which makes it easy for developers to manage and customize boilerplates, based on the nature of their project. It handles internal boilerplate management and makes it easy to add dynamic structures to boilerplates.

So what does this exactly mean? Consultant will help you to make sure you do not have to write a single line of boilerplate code anymore. You start consultant, answer some questions about some aspects of a specific boilerplate and you are ready to take off. Coolest thing? It is language independent.

Setup

Installation

Since consultant is distributed via npm, you can install consultant-cli globally via the node package manager. Run the version option after installation and compare your version with the one on this website to make sure you are reading the correct documentation for your version. If you would like to remove the toolkit, make sure to also remove the configuration file and cache folder.

# Install Consultant
$ npm install -g consultant-cli
$ cst --version
# Remove Consultant
$ npm uninstall -g consultant-cli
$ rm -rf ~/.cst-boilerplates ~/.consultant

Configuration

When you run consultant for the first time, it will make sure that the default configuration file and cache folder exist. Use the HELP command to get a small overview of the possible commands and to make sure that the configuration file exists. Afterwards, open de configuration file and note that there are a lot of default values. All of these will come clear in the sections below. Make sure that the created boilerplate cache is empty.

$ cst help
$ vim ~/.consultant
$ ls ~/.cst-boilerplates

Basics

Boilerplates

The first step is to add some static boilerplates to consultant. A static boilerplate is a simple set of files that can be used as a starting point to kickstart a new project. Note that you can choose a boilerplate in the language of your choice. For ease of use we will use this boilerplate. Simply add it with the ADD command. The last argument is the name to call the installed boilerplate. In case no name is given, consultant will ask for one.

# Install an online boilerplate
$ cst add 'https://github.com/gaearon/library-boilerplate' 'node-lib'

# Install a local boilerplate
$ cst add . 'local-bp'
$ cst add /an/absolute/path 'local-bp'
$ cst add ../../../a/relative/path 'local-bp'

Remove

It would not make much sense if we could add boilerplates, but couldn't remove them. Removing a boilerplate can be done with the REMOVE command. You should also provide the name of the boilerplate as an extra argument. Again, if no name is provided, consultant will ask for one.

$ cst remove 'node-lib'

Browse

Once you have added some boilerplates, it should be fun to see a list of the installed boilerplates. Do this at any time with the LIST command. The names of the different boilerplates should be unique. This command can be very useful to verify new names in these scenarios.

$ cst list

Create

Now that we can manage our boilerplates, let's start using them. Kickstarting a new project is very easy with the CREATE command, followed by the name of the boilerplate we would like to use. Consultant will now craft the new app in the current working directory. You can also provide an extra argument to the CREATE command, which will be used as the relative path to the directory where the boilerplate should be copied to. If it does not exists, consultant will create it for you.

# Manual Creation
$ mkdir scraper && cd scraper
$ cst create 'node-lib'

# Automated Creation
$ cst create 'node-lib' scraper

Advanced

Defaults

The real value of consultant is that you can customize boilerplates at runtime. This means that whenever you kickstart a new project with the same boilerplate, it might be customized automatically based on the needs of your current project. In order to set this up, you should add a consultantfile.js to the root folder of your boilerplate. The most basic consultant file should look like the one below. In case you would like to follow along, you can check the cst-markdown-tutorial. This is a basic example of a customized boilerplate for consultant, which makes it easy to understand the configuration interface.

export default (template) => {};

Source

Before we proceed, you should understand the fundamental difference between a static and dynamic boilerplate. Whenever no configuration file is found in the root of the boilerplate, consultant assumes that it is a static one. This means all files from the root of the boilerplate will be copied when kickstarting a new project. When we have a dynamic boilerplate, we have to specify where the real source code of the boilerplate is located. This folder is called boilerplate in the cst-markdown-tutorial. You can set this to whatever you want it to be. You may decide not to define it, in which case the default folder is used. This is not advised because default folders might change from machine to machine, in which case your boilerplate might break.

export default (template) => {
  template.useSourceFolder('boilerplate');
};

We will be talking a lot about the configuration files in these sections. Note that whenever we are talking about the ~/.consultant file, we will refer to it as the main configuration file, while the consultantfile.js is the configuration file of your boilerplate. All JavaScript configuration code you will see, should be located in the boilerplates configuration file.

Variables

While generating a dynamic boilerplate, it is possible to present certain questions to the user in order to have a better understanding of the nature of the project. Consultant uses the inquirer module to ask questions to the user. Take a look at the examples from the repository to get a feel of which types of questions can be used. You can simply pass these questions to template.ask(). Here is an example where we ask for the name of the user.

export default (template) => {

  template.useSourceFolder('boilerplate');

  template.ask([{
    type: 'input',
    name: 'name',
    message: 'Your name?',
    validate: input => input.length > 3 ? true: 'Min 4 chars required!',
  }]);

};

When this boilerplate is generated, the user is first asked the given list of questions. After the interview, all these variables are available from within the files of the specified source folder. Consultant uses Mustache to render the boilerplate. Make sure to check the documentation for all possible syntax. An example C file might now look like the one below. Underneath, I have added a possible rendered file when the user answered the question with 'John Doe'.

/* boilerplate/main.c */
#include<stdio.h>
main() {
  printf("Hello, {{ name }} !");
}
/* main.c */
#include<stdio.h>
main() {
  printf("Hello, John Doe !");
}

You can do a whole lot more than just inserting variables. Everything that's possible with the default mustache rendering engine, is possible with consultant. I advise to take a look at the cst-markdown-tutorial, where I used negations and loops, which might help you along the way. There are a centillion tutorials online about mustache because it already exists for a while, which makes it easy to find whatever you need.

Delimiters

The default templating syntax of mustache is great, but sometimes you just can't use it. When you are creating an Angular boilerplate for example, you just can't use the double bracket notation for your dynamic boilerplate. That's why you can override them. It is for these cases that consultant has the template.setStart() and template.setEnd() functions. Below you can see how easy it is to override the delimiters and use adapt them for your project. We use the same example as the one used earlier, but we adapted the delimiters for now. The defaults are still the double brackets, because I feel they are the most natural way of creating templates.

export default (template) => {

  // ...

  template.setStart('<<<');
  template.setEnd('>>>');

};
/* boilerplate/main.c */
#include<stdio.h>
main() {
  printf("Hello, <<< name >>> !");
}
/* main.c */
#include<stdio.h>
main() {
  printf("Hello, John Doe !");
}

Filters

As you have seen before, it is possible to use if-statements and loops in the templates. However, often enough you would just like to not generate a file at all based on some chosen variable of the user. In this case, the template.filter() comes into play. In the example below, extra.md will only be generated if the user replied 'yes' to the confirmation question. The argument of the filter validation function will always be a dictionary with all the chosen variables of the user.

export default (template) => {

  // ...

  template.ask([{
    type: 'confirm',
    name: 'extra',
    message: 'Would you like the extra file?',
    default: false,
  }]);

  template.filter('extra.md', input => input.extra)

};

We should also state that the order of the functions in the boilerplate configuration file does not matter for consultant. This is due to the delayed execution. The functions just set the parameters for the process. The process itself will only start when the configuration function is finished.

Filters

You can also make sure that some files don't get parsed by the render function. You can do this with the `dontTouchExtensions` function.

export default (template) => {

  // ...

  template.dontTouchExtensions(['.png']);

};

Secrets

Consultant provides a very easy way to hide files from a boilerplate generation. This can be useful when dealing with environment files, password files, etc. Hiding files or directories is as simple as calling template.ignore() and template.ignoreRecursive(). In the example below, we do not render the .env file or anything inside the secret folder. It should be clear that this is the same functionality as passing a function to the filter method that always returns true.

export default (template) => {

  // ...

  template.ignore('.env');
  template.ignoreRecursive('secret');

};

Summaries

You now know how to kickstart a new project with consultant. Consultant provides an option to add an introduction and summary to the generation process. This makes it possible to improve the user experience and provided feedback about certain parameters used in the process. You can set them with template.setIntroduction() and template.setSummary(). An introduction is just a string, but a summary is a synchronious callback that returns a string which makes it possible to use variables used in the setup process. The passed parameter will be a dictionary with all chosen variables of the user.

export default (template) => {

  // ...

  template.setIntroduction('You are going to create the example markdown template!');
  template.setSummary(input => 'Done, ' + input.name + '!');

};

Examples


Workflow

Help

You can run the HELP command to show the most recent weblinks to the documentation or the issues page. It should also show some frequently asked questions to get you started with the consultant tool. A list of commands will also be printed, in case you forgot one!

$ cst help

Batch

Managing a lot of boilerplates can still be an awkward amount of work. That's a fair enough reason for consultant to support adding multiple boilerplates at once with the BATCH command. This command asks for a json file as input, which should have the structure of the one below. You can list as many boilerplates as you wish, together with the name for consultant. In case no name, an invalid name or a duplicate name is given, consultant will pause the process to ask for a new one.

{
  "data": [{
    "url": "https://github.com/gaearon/library-boilerplate",
    "name": "node-lib"
  }]
}
$ cst batch batch.json

Export

You can also export the current consultant setup to a batch file so you can easily make backups or copy configurations to other machines. When no output location is provided, the batch file will be printed. Note that only repositories can be exported.

$ cst export
$ cst export batch.json

Reset

Being able to add a lot of boilerplates with one single command makes it easy to make backups of your configuration. Together with this command, I have create a simple RESET command which resets and removes all boilerplates of consultant. This makes it easy to remove them all and then restoring a workspace with the batch command. Consultant will ask for confirmation when you are about to reset. To precent this, add the hard option to the command arguments.

$ cst reset --hard

Update

An extra advantage of using online boilerplates is that you can update all of them with one single command. Use UPDATE to make sure all of your boilerplates are updated to their newest versions. This is especially useful to update multiple boilerplates at once.

$ cst update

Verbose

It might be useful to run in VERBOSE mode to know exactly what consultant is doing. The verbose option can be added to any command. It uses winston for logging and will set the log level to debug when the verbose option is detected.

$ cst list --verbose

Snapshot

The last possible command of this version of consultant is the SNAPSHOT command. This basically snapshots the current working directory and stores it as a boilerplate. This can be useful when you are working on a project and, when the boilerplating is finished, you snapshot the root of your project so you can use it again at a later point in time. This has the same effect as adding the current working directory.

$ cst snapshot

Contribute

Code of Conduct

Everyone is encouraged to contribute to consultant. The project is new and might become a lot better if everyone would add some features. I am not constantly working on this project as I have other stuff to do, so feel free to step in. Before you fork and start implementing a feature, make sure to discuss it here. That way we might already clear some details out about why or why not I would accept the pull request. Please make sure to follow some basic guidelines as shown below.

  • Be careful
  • Be considerate
  • Be friendly and patient

Semantic Versioning

This repository uses semantic versioning. Releases are automatically released with semantic release. I release patch versions for bugfixes, minor versions for new features, and major versions for any breaking changes. When there are breaking changes, I also introduce deprecation warnings in a minor version so that users learn about the upcoming changes. In case there is something wrong because of the automated releases, please report it as fast as possible.

Conventions

I use the popular Airbnb style guide. Make sure you do too in order for you pull request to get accepted. To check if you code is conform these conventions, run the lint command. If you don't know how to contribute with pull requests, check this awesome tutorial out.

$ yarn lint

Development

After cloning the repository, install all the dependencies and run the prepublish command. This command will make sure eslint and tests did run and will build a new version into the dist folder. Use the commit command to commit. Instead of the prepublish command, you can also run all the commands yourself if you do not need to run them all. This might speed things up a bit.

# Using the automated workflow
$ yarn install
$ yarn prepublish
$ yarn start -- --version
$ yarn commit

# Using the manual workflow
$ yarn install
$ yarn lint
$ yarn test
$ yarn build
$ yarn start -- --version
$ yarn commit