![](https://i.imgur.com/RL2kYyg.png)
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
New Examples 🤓
At the time of writing I am working on a couple of useful dynamic boilerplates for node. They should be finished in about two weeks. When they are ready, I will post them here. If you have some other nice examples, let me know!
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
New Workflows 🔥
In case the current workflow is too awkward for you and you have a proposal for another way of working, let me know. There might be an easier way to handle boilerplates, but this was the most straightforward to work with after a couple trials.
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
Thank You! 🎉
Thanks for using this tool. If you really like it, please spread the word. If this is not the case at all, I would appreciate it if you would share the reason. That way I might be able to learn a thing or two and improve consultant so it fits your needs.