How to easily configure your Typescript project with Prettier and ESLint

How I went from the curiosity of building a Node.JS CLI to deploy it as a free to use NPM package in a rainy day.

Foto de Karolina Grabowska no Pexels

For a few days I am not sure why, but the idea of creating a CLI using Node.JS was in my mind… I just didn’t know what to create as a proof of concept that… wouldn’t take too much effort. Then I thought about a classical problem: project scaffolding using Node.JS…

There are a lot of frameworks that already give to you out of the box the Prettier and ESLint configuration, such as NestJS. There tools are great to maintain your code well organised and quite often are used as a quality gates for accepting pull requests in your project. But you might work in some frameworks that doesn’t have this support by default, or … even working in a legacy project where you need to add them by yourself. The process, after you already configured several times in your life, is straight forward and brainless to do… That’s how it become my humble CLI called makin-cli.

It is a CLI that you can use installing it globally with npm i -g makin-cli

I truly believe in the expression “Skin in the game” which made myself to use my own cli to finish my own cli. In short, I create the CLI using Typescript and not configuring Prettier neither ESLint but I finish adding it using my own built cli o/

To create any CLI is quite simple to start. Since I used Typescript, I first created a new npm project with npm init, followed by adding the Typescript configuration with a tsconfig.json like

{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2020",
"lib": ["ES2020"],
"moduleResolution": "node",
"sourceMap": true,
"outDir": "./lib",
"baseUrl": "./",
"incremental": true
}
}

Adding the required packages:

npm i --save-dev ts-node typescript

then adding this to the package.json

"main": "./lib/index.js",
"bin": {
"makin-cli": "./lib/index.js"
},

That should be enough to start coding in Typescript.

To start building your CLI, you need to install the npm package called commander

npm i --save commander

Commander package, as the owner describes, is “The complete solution for node.js command-line interfaces.”

With that package, you can set your entry point with versioning, description, setting up the options you will allow as a CLI and to take these options from the command line.

My initial configuration ends like the following:

program
.version('0.0.7')
.description('A CLI that scaffolds some quality gates in your app')
.option('-p, --prettier', prettierLintService.getPrettierDescription())
.option('-l, --lint', prettierLintService.getLintDescription())
.parse(process.argv);

The two added options are:

  • -p: or --prettier which will configure Prettier to you
  • -l: or --lint which will configure Lint to you

To make it to work, you will need to continue coding with the following:

if (!process.argv.slice(2).length) {
program.outputHelp();
} else {
console.info('Welcome back ;)');
const options = program.opts();
if (options.prettier) await prettierLintService.configPrettier();
if (options.lint) await prettierLintService.configLint();
}

the outputHelp function will show to the user a how to use it in case he will not add any option. In a sample of this will be the following:

➜  makin-cli git:(main) ✗ makin-cli 
[22:37:09] [info] _ _ _ _
_ __ ___ __ _ | | __ (_) _ __ ___ | | (_)
| '_ ` _ \ / _` | | |/ / | | | '_ \ _____ / __| | | | |
| | | | | | | (_| | | < | | | | | | |_____| | (__ | | | |
|_| |_| |_| \__,_| |_|\_\ |_| |_| |_| \___| |_| |_|

Usage: makin-cli [options]
A CLI that scaffolds some quality gates in your appOptions:
-V, --version output the version number
-p, --prettier add prettier to package.json, with a format script and a generic configuration file (.prettierrc)
-l, --lint add eslint to package.json, with a lint script and a generic configuration file (.eslintrc.js)
-h, --help display help for command
➜ makin-cli git:(main) ✗

Now, we can combine -p and -l or use then separate. A quick sample would be:

➜  makin-cli git:(main) ✗ makin-cli -p -l
[22:38:01] [info] _ _ _ _
_ __ ___ __ _ | | __ (_) _ __ ___ | | (_)
| '_ ` _ \ / _` | | |/ / | | | '_ \ _____ / __| | | | |
| | | | | | | (_| | | < | | | | | | |_____| | (__ | | | |
|_| |_| |_| \__,_| |_|\_\ |_| |_| |_| \___| |_| |_|

[22:38:01] [info] Welcome back ;)
[22:38:01] [info] Configuring prettier...
[22:38:01] [info] Installing prettier
[22:38:02] [info] Adding .prettierrc
[22:38:02] [info] Adding format script to package.json
[22:38:02] [info] Configuring lint...
[22:38:02] [info] Installing eslint
[22:38:04] [info] Configuring .eslintrc
[22:38:04] [info] Adding lint script to package.json
➜ makin-cli git:(main) ✗

But how it worked? To work I split the code into two functions:

  • configPrettier: where I used a mix between child_process.exec function and fs.writeFileSync to create files. I also used a time saver package called npm-add-script which allowed me to add scripts to package.json dynamically. Last but still important, I also added a string with the representation of the prettier config file that I used to create the .prettierrc file. The code looks like:
console.info('Installing prettier');
await exec('npm i --save-dev prettier');
console.info('Adding .prettierrc');
await writeFileSync('.prettierrc', prettier);
console.info('Adding format script to package.json');
npmAddScript({ key: 'format', value: 'prettier --write "src/**/*.ts"', force: true });
  • configLint: the approach is the same as Prettier but with the follow ending:
console.info('Installing eslint');
await exec('npm i --save-dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser');
console.info('Configuring .eslintrc');
await writeFileSync('.eslintrc.js', eslintrc);
console.info('Adding lint script to package.json');
npmAddScript({ key: 'lint', value: 'eslint "{src,apps,libs,test}/**/*.ts" --fix', force: true });

The full working sample, that you can also contribute with new commands if you feel like is here.

Finally, to publish the package to npm, you just need to log into npm and run the following:

npm publish

As I put before, the package can now be found here.

Here is a way to see it in action!

Adding Prettier and ESLint in action!

In this article I wanted to show a mix between trying to give you more ideas for doing PoC’s (proof of concepts) and to share this humble CLI that anyone can use if it makes sense. If you are creative enough you can start building CLI’s and scripts that can do a real deal in real life scenarios.

If this CLI by any chance can be a good call for you project, feel yourself free to install and use it!

I didn’t make a deep explanation on how do create it but I hope the hints allows you to try by yourself. Also, all the source code is open source and you can find it in my GitHub repository.

Want to learn more about Typescript, specially on GraphQL or REST APIs? Read my other articles!

For GraphQL

For REST API’s

Bonus: Kafka

Thanks for reading, don’t forget to give a comment/like if this article was anyhow useful for your studies! See you soon

Software Engineer