Back
Chapters

Linting and Formatting Code

Search icon
Search Book
⌘K

Wheel from BigBinary

BigBinary's wheel project is the source of truth for all these configs and toolchains.

Every internal product or Rails project in BigBinary uses wheel as the base for bootstrapping that new project.

Thus if you are asked to create a new Rails project while working in BigBinary, then you should strictly add the below mentioned hooks and relevant configs into your project.

Rubocop

Rubocop is a linter as well as a code formatter specifically for Ruby.

BigBinary has strong and opinionated coding style guidelines that are enforced using this tool.

Add the gems

First, let's add the relevant gems to our Gemfile:

1# previous gems as it was
2
3group :development, :test do
4  # previous gems under this group as it was
5
6  # For code formatting and linting
7  gem "rubocop"
8  gem "rubocop-rails"
9end
10# other gems if any

Now install the gems by running the following from the terminal:

1bundle install

Please note that when we added the gems to Gemfile we had added it under the development and test groups only.

Add the config

Add BigBinary's Rubocop config to the root of your project by running the following command from the terminal:

1curl -o ".rubocop.yml" "https://raw.githubusercontent.com/bigbinary/wheel/master/.rubocop.yml"

Running Rubocop on all Ruby files

Run the following from the root of the project in your terminal:

1bundle exec rubocop

The above command would output the offenses it finds. Some offenses are auto-correctable by Rubocop. But some are not.

We auto-correct the correctable ones by running:

1bundle exec rubocop -a

That should fix all the safely correctable errors. The non-corrected ones should be manually corrected.

Moving forward, we won't be running these commands. Rather we will use git hooks to run these commands for us on modified files.

Setup pre-commit Git hook

A pre-commit Git hook can re-format the files that are marked as "staged" by git add command before you commit.

In BigBinary, no PR should be made without running the pre-commit hook. Or more subtly saying, please don't bypass the Git hooks.

Let's set up husky to run the hooks and install lint-staged to lint the files.

Run the following commands from the terminal:

1npx husky-init && yarn # press y to proceed
2yarn add -D lint-staged

Now add the following highlighted lines to the start of your package.json file.

Note that you need to retain all other keys which were already part of the file as it was.

1{
2  "lint-staged": {
3    "app/javascript/src/**/*.{js,jsx,json}": [
4      "prettier --write",
5      "eslint --fix",
6      "git add"
7    ],
8    "{package.json,.eslintrc.js,.prettierrc.js}": [
9      "prettier --write",
10      "eslint --fix",
11      "git add"
12    ],
13    "./**/*.rb": [
14      "bundle exec rubocop -a"
15    ],
16    "{Gemfile,Rakefile,config.ru}": [
17      "bundle exec rubocop -a"
18    ]
19  },
20  ...
21  <rest of the keys as it was>
22}

Husky, by default, will read the Git hooks from the .husky directory within the root of the project.

When we installed husky, it should've automatically created the .husky folder along with some default configs.

Now copy-paste the whole code block into your terminal and execute it, in order to setup the hooks:

1cat << 'EOF' > .husky/pre-commit
2#!/bin/sh
3. "$(dirname "$0")/_/husky.sh"
4. "$(dirname "$0")/helpers/lint_staged.sh"
5
6lint_staged_files
7EOF
8chmod a+x .husky/pre-commit
9curl --create-dirs -o ".husky/helpers/lint_staged.sh" "https://raw.githubusercontent.com/bigbinary/wheel/master/.husky/helpers/lint_staged.sh"

In the following sections, we will set up the tools which our hooks depend upon.

ESLint

ESLint is a static code analysis tool to quickly find problems with the JavaScript codebase.

Most of the problems ESLint finds can be automatically fixed.

Add the modules

Run the following command from the terminal:

1yarn add -D eslint \
2@babel/eslint-parser \
3eslint-plugin-react-hooks \
4eslint-plugin-import \
5eslint-config-prettier \
6eslint-plugin-prettier \
7eslint-plugin-json \
8eslint-plugin-react \
9eslint-plugin-promise \
10eslint-plugin-jam3 \
11eslint-plugin-cypress \
12eslint-plugin-unused-imports

The above command installs the required ESLint plugins.

Prettier is a code formatter that can be integrated with linters. To integrate prettier with ESLint, we added eslint-config-prettier as well as eslint-plugin-prettier modules.

Add the config

The ESLint configs are modularly handled in the wheel repo.

We have to bring in all of those configurations into granite.

We also have to manually add a .eslint-rules/custom.js file, which is where all the custom ESLint rules that differs from wheel should be added.

Copy paste the following into the terminal and run it:

1raw_base_url="https://raw.githubusercontent.com/bigbinary/wheel/master"
2declare -a configs=(
3  ".eslintrc.js"
4  ".eslint-rules/helpers/index.js"
5  ".eslint-rules/imports/enforced.js"
6  ".eslint-rules/imports/order.js"
7  ".eslint-rules/globals.js"
8  ".eslint-rules/overrides.js"
9  ".eslint-rules/promise.js"
10  ".eslint-rules/react.js"
11)
12for config in "${configs[@]}"; do
13  echo "Downloading ${config}..."
14  curl --create-dirs -o "${config}" "${raw_base_url}/${config}"
15done
16cat << 'EOF' > .eslint-rules/custom.js
17module.exports = {};
18EOF

Prettier

An unformatted JavaScript file is really hard to read. Prettier helps us in keeping the code sane and consistent.

Add the module

Run the following from the terminal:

1yarn add -D prettier

Also add the prettier plugin for tailwind using the following command:

1yarn add -D prettier-plugin-tailwindcss

Add the config

Run the following from the terminal to fetch the Prettier config from wheel:

1curl -o ".prettierrc.js" "https://raw.githubusercontent.com/bigbinary/wheel/master/.prettierrc.js"

Formatting all JavaScript files

In the Rails codebase we can run the following command from terminal, in order to format all Javascript files:

1npx prettier --write "./app/javascript/src/**/*.{js,jsx,json}"
2npx eslint --fix "./app/javascript/src/**/*.{js,jsx,json}"

Moving forward, we won't be running these commands. We will use git hooks to run these commands for us on modified Javascript files.

Configuring VSCode settings

Now that we have added the configs for the relevant tools, let's add the necessary VSCode settings to ensure these tools are run on the fly while we code.

At BigBinary we use the following plugins along with some custom settings in VSCode, to ensure coding style is maintained from grassroots level.

Now, copy-paste the following commands into your terminal and run them:

1curl --create-dirs -o ".vscode/extensions.json" "https://raw.githubusercontent.com/bigbinary/wheel/master/.vscode/extensions.json"
2curl -o ".vscode/settings.json" "https://raw.githubusercontent.com/bigbinary/wheel/master/.vscode/settings.json"

Running above commands should've created the relevant configs.

Now, open the granite project in VSCode, by running the following from the root of the project:

1code .

If you had the project already opened in VSCode, then restart VSCode for the settings to take effect.

At the bottom right side, you should will get a popup for installing the recommended extensions. Go ahead and click the install button:

VSCode extensions recommendation.

After installation, all the extensions should be working out of the box with the required settings.

You can visit a Ruby file and see Rubocop provide you live linting. Likewise, you can visit a JavaScript file and format it via VSCode to see Prettier in action.

Adding .editorconfig

We can specify consistent IDE configurations for our project in the .editorconfig file located in the root of the project. This ensures the code style remains uniform across the project when multiple users are working on the same project.

We can use the same config from wheel.

Run the following command from the terminal:

1curl -o ".editorconfig" "https://raw.githubusercontent.com/bigbinary/wheel/master/.editorconfig"

VSCode will automatically detect this configuration, and will strictly adhere to the rules mentioned in it.

Running Git hooks

You don't need to manually run the Git hooks. These hooks are run automatically based on the kind of hook we have added.

Currently, we have added a pre-commit hook. Thus before each time, you try to commit new code to the project, the hook will verify the coding style follows our guidelines.

The Git hook will only be run on files that are currently modified. Meaning, the hook will ignore all files which were already committed using Git.

That is why we mentioned a previous section on how to run the formatting on all files, which is inclusive of the committed files, if need be.

If the Git hook fails, then it means that you need to fix a particular section within your codebase manually.

Once again, never bypass the Git hooks. Always run the Git hooks, resolve the errors and then only push to GitHub.

Git hooks in action

Let's commit our code to see the Git hooks in action.

Run the following from the terminal:

1git add -A
2git commit -m "Added git hooks, eslint, prettier and rubocop"

Ideally, the above command should run without any issues.

But let's take the case where we missed out on adding the string literal comment as the first comment in the file in our Gemfile.

In such a case we might get the following output in the terminal: Rubocop string literal error.

Rubocop shows an offense when it can't automatically fix the offense.

The first thing we have to note is which task failed in the hook.

Currently, it's the task for {Gemfile,Rakefile,config.ru} that has failed.

The next thing to look into is the offenses and check which offense has the keyword [Correctable].

In the above case, the offense is in the Gemfile since we are missing the string literal comment in it.

For above mentioned scenario we can run the following command from the terminal:

1bundle exec rubocop -A Gemfile

Note that we have run the unsafe auto-correction mode from Rubocop in the last command.

It's not recommended to be used often. That's the exact reason why we haven't added this command as part of our hook. The offenses shown by Rubocop are best fixed manually.

If you had encountered the above error, then now you should be able to commit, by running the following commands from the terminal:

1git add -A
2git commit -m "Added git hooks, eslint, prettier and rubocop"

References