Adding code formatting, linting, pre-commit hooks and beyond...
Tags: webdev, javascript, opensource, programming
Published: 2023-11-01 15:28:08

## Project 
[Node-TILify](https://github.com/sdthaker/Node-TILify)

## Commit
[7bf78de7](https://github.com/sdthaker/Node-TILify/commit/7bf78de7d36fcad63987551ae3d7097035876955)

## Lab
This week I had to set up and configure [Prettier](https://prettier.io/), [ESLint](https://eslint.org/), [Husky](https://typicode.github.io/husky/) & [lint-staged](https://github.com/lint-staged/lint-staged#readme) dependencies to my codebase for better code formatting and linting and VSCode integration with prettier, eslint and pre-commit hooks using husky and lint-staged for achieving automation for linting and formatting.

## What is Prettier?
It is a code formatting tool that helps developers format code as they like by setting up config files, for example, automatically converting all double quotes to single quotes.

## How did I set up Prettier?
Prettier has pretty good [documentation](https://prettier.io/docs/en/install) so I started with that. The steps involved in setting up Prettier were:  

* Added the [prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) for VSCode.

* Installed it as a dev dependency by running the command, `npm install --save-dev --save-exact prettier`.

* Added `.prettierrc` file on the root level, with these overridden default settings,
```javascript
{
  "tabWidth": 2,
  "useTabs": false,
  "bracketSameLine": false,
  "bracketSpacing": true,
  "embeddedLanguageFormatting": "auto",
  "endOfLine": "lf",
  "htmlWhitespaceSensitivity": "css",
  "proseWrap": "preserve",
  "printWidth": 80,
  "semi": true,
  "singleAttributePerLine": true,
  "singleQuote": true,
  "trailingComma": "es5"
}
```

* Added `.prettierignore` file which ignores files/directories that are listed in it. For example, `node_modules` folder is not our code so we're better off not checking for code style in that folder. I ignored these files and folders in my project:
```javascript
package.json
package-lock.json
node_modules/
examples/
```

* Added an npm script to help users run prettier with a single command, `npm run prettier` which in turn runs the command `prettier --write .`

Besides, I formatted my code using this tool. It showed 2 errors. One was about the HTML's link tag not being closed. The fix was to put the href value in quotes and add a forward slash at the end of the tag. Also, it formatted all HTML files that my program generated which was pretty nice since I had trouble figuring out how to format those files within my code logic. Furthermore, I updated the CONTRIBUTING.md file with instructions on how to run the formatter.

## What is ESLint?
It is a code linting tool that helps developers lint code as they like by setting up config files, for example, automatically adding a semi-colon after each line in a JS project.

## How did I set up ESLint?
ESLint has pretty good [documentation](https://eslint.org/docs/latest/use/getting-started) so I started with that. The steps involved in setting up ESLint were:  

* Added an [eslint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) for VSCode.

* Installed it as a dev dependency by running the command, `npm install --save-dev eslint`.

* Added `.eslintrc.cjs` file on the root level, with these default settings,
```javascript
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  "extends": [
    "eslint:recommended",
    "prettier"
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
};
```

* Added `.eslintignore` which ignores files/directories that are listed in it. For example, `node_modules` folder is not our code so we're better off not linting code in that folder. I ignored these files and folders in my project:
```javascript
package.json
package-lock.json
node_modules/
examples/
```

* Added an npm script to help users run eslint with a single command, `npm run lint` which in turn runs the command `eslint .`. It'll fix all the auto-fixable errors and pick the ones that need your attention.

Besides, I linted my code using this tool. It showed 2 errors. One was about process variables not being defined in a lot of files. `process` is a global variable easily accessible in Node.js project. ESLint wasn't aware of the language/environment that I was using in my project so I added `node: true` as part of `env` object in `.eslintrc.cjs` file to allow ESLint to import global vars automatically. Another error was about 2 redundant backslashes in a regex that I have in my code. I removed them and tested the code thoroughly to make sure everything was still operating properly. Furthermore, I updated the CONTRIBUTING.md file with instructions on how to run the linter.

## Integrating ESLint with Prettier

As I was reading the Prettier documentation I stumbled upon [this blob](https://prettier.io/docs/en/install#eslint-and-other-linters) which talks about how to make ESLint and Prettier play nicely with each other. It instructed to install [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) dependency in my project using the command `npm install --save-dev eslint-config-prettier`. Then I added prettier as part of the `.eslintrc.cjs'` `extends` array making `prettier` dependency part of the linting process. Lastly, I ran its cmd line helper `npx eslint-config-prettier path/to/main.js` to check if there were any ESLint rules which are unnecessary or would conflict with Prettier. The test results came out fine with no unnecessary or conflicting rules. The use case on how it works and why to use it is mentioned [here](https://github.com/prettier/eslint-config-prettier#what-and-why).

## Integrating ESLint and Prettier into VSCode

I got the inspiration to add this integration task from my open-source course professor David and the telescope project. The purpose of this task was to add as much automation in the code formatting and linting as possible. I set up both tools in VSCode such that the code gets formatted and linted automatically when the user saves the file. I added `.vscode` directory on the root level, which helps VSCode read and override the default user settings to apply the settings listed in the files located in this directory. It contains 2 files, `settings.json` and `extensions.json`.

* `settings.json` - This file sets VSCode's default formatter to prettier, enables format on save to beautify file formatting using prettier, enables code actions on save to fix all auto-fixable code errors using eslint, and automatically detects and uses the eslint configuration from the project's root directory. The JSON configuration object is added below, 

```javascript
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll": true
  },
  "eslint.workingDirectories": [{ "mode": "auto" }]
}
```

* `extensions.json` — This file suggests a new contributor to your project to install eslint and prettier as extensions, the first time they open the project in the VSCode. The JSON configuration object is added below, 

```javascript
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
  ]
}
```

## What are Pre-commit hooks?
Hooks are essentially functions that hook into your git workflow and are triggered before or after a certain event is fired. For example, to automate the process of code formatting and linting, which was the sole reason to add this task to my project, we can set up hooks that would run just before a piece of code is committed in the git workflow. This ensures that the code being committed is consistent and free of errors which the human eye could miss.

## How did I set up Pre-commit hooks?
I used 2 dependencies to set this up, namely [Husky](https://typicode.github.io/husky/) & [lint-staged](https://github.com/lint-staged/lint-staged#readme). Husky allows you to run hooks for every git-commit action whereas, lint-staged runs specified scripts when it matches staged files. The stages involved in this process were:

* Installing and initializing husky using the command `npx husky-init && npm install`. It creates a `.husky` folder at the root level and creates a `.husky/pre-commit` hook shell script file which runs a command before each commit. One of the benefits of lint-staged is that it will run the commands that you define for the chosen staged files and also immediately add the changes to the staging phase if possible. It also allows you to run the linting and formatting in parallel to multiple staged files hence saving time when there are many staged files to commit.

* Installing lint-staged and configuring it with Husky. Ran the command `npm install --save-dev lint-staged` to install the dependency and updated the `npx test` command in `.husky/pre-commit` to `npx lint-staged`. This allows Husky to run lint-staged which in turn runs the configuration file to perform linting and formatting operations. 

* Adding a `.lintstagedrc.json` file to set up linting and formatting commands. This file contains rules about which tools to run for which specific files. Also lint-staged automatically saves the changes performed during the formatting and linting operation before it adds the files to the commit stage. The rules set up for this tool were to run prettier and eslint (fix all auto-fixable issues) on javascript files and prettier on html and css files. The file contents are below,
```javascript
{
  "*.js": ["prettier --write", "eslint --fix"],
  "*.{html, css}": ["prettier --write"]
}
```

## Learning Outcomes
This was a wonderful experience for me to add all these tasks in my codebase. I always felt intimidated when it came to setting up these tasks using config files because I never integrated it in any of my previous projects. But time and time again the software development field makes me realize that the power of reading documentation is incredible. Almost all of the above integrations came from reading documentation for these tools.

Besides, developer experience (DX) is one of the key issues that everyone is trying to address these days. If the developers contributing to your open source project or developers in your team at work aren't happy then it'll affect code quality which is bad in general. One of the ways to improve DX is through code consistency, implemented using linting and formatting by following same rules across the project. Also, it helps with code readability & maintainability since it's much easier to read a piece of code written in same style across the board and it makes the code robust and bug resistant. Furthermore, having automation in your project allows for more and more developers joining your movement and contributing to it. Keep in mind, the more automation you have in your code the more it gets adopted and used.