Setting Up a Node & TypeScript Project - Configure Tooling

Published on

Something that I’ve noticed while working in the JavaScript ecosystem is the vast number of tools available to developers. Being a relatively new developer, this was very overwhelming at first. Fortunately, the majority of them are easy to setup and once you have them setup you won’t need to make changes to them frequently. Some of these tools allows for faster, more consistent development. With this next post we will be configuring some common tools used in Node/JavaScript development.

 

Prerequisites

 

Setting Up Linting With Eslint

When working with other developers, each developer may have a specific code formatting style that they follow. A simple example is one developer might use single quote while another might use double, or event the infamous tabs vs spaces debate. Linters/linting provides developers a way to enforce style standard/guidelines across a codebase to ensure all code is formatted/styled in a specific way regardless of a developer’s personal style.

We are going to setup eslint and prettier to format/style our codebase by running a npm command. Later we’ll setup husky and lint-staged to tie into git hooks and run our linter pre commit.

First we need to install our dev dependencies. With eslint/prettier there are quite a few. Refer to the provided table for explanation of each dependency.

Package Name Description
eslint The core linter package
@typescript-eslint/parser A typescript parser for eslint
@typescript-eslint/eslint-plugin A set of eslint rules for typescript
eslint-plugin-prettier Plugin to allow prettier errors to display as eslint errors.
eslint-config-prettier Disables eslint rules that conflict with prettier
prettier The core prettier package

Install our dependencies…

npm install --save-dev eslint \
  @typescript-eslint/parser \
  @typescript-eslint/eslint-plugin \
  eslint-config-prettier \
  eslint-plugin-prettier \
  prettier \

We will need to create three files, two configuration files and one ignore file. At the root of our project, create these files…

touch .prettierrc .eslintrc .eslintignore

Our .prettierrc is pretty simple. Feel free to add/remove prettier options as you wish

{
  "semi": true,
  "trailingComma": "all",
  "singleQuote": true,
  "printWidth": 120,
  "tabWidth": 2
}

And our .eslintrc file

{
  "parser": "@typescript-eslint/parser",
  "extends": [
    "plugin:@typescript-eslint/recommended",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "rules": {
    "prettier/prettier": ["error", { "singleQuote": true }],
    "@typescript-eslint/explicit-function-return-type": "error",
    "@typescript-eslint/no-explicit-any": 1,
    "@typescript-eslint/no-inferrable-types": ["error", { "ignoreParameters": true }],
    "@typescript-eslint/interface-name-prefix": ["error", { "prefixWithI": "always" }]
  },
  "env": {
    "node": true
  }
}

To learn more about all the eslint config option, you can refer to their great documentation.

And finally our .eslintignore. This file works like a .gitignore. It tells eslint the files/directories to ignore from linting. If you don’t want to have an additional .eslintignore file you can tell eslint to use your .gitignore (the .gitignore use the same syntax as .eslintignore) by adding --ignore-path .gitignore when calling eslint in your package.json script.

Our .eslintignore…

node_modules/*
dist/*
coverage/*

Now that eslint is configured, we can add our scripts to a package.json to run the linter.

{
  ...
  "scripts": {
    "test": "jest",
    "coverage": "jest --coverage",
    "lint": "eslint",
    "lint-fix": "npm run lint -- --fix", 
    "start": "ts-node src/index.ts",
    "build": "tsc"
  },
  ...
}

We added two additional scripts. One, lint, which will run the linter and prints the errors to the consolel. The other, lint-fix, which will run our lint script with an additional option --fix to auto-correct any errors that occur.

If we run npm run lint we will see a few errors in our console.

Lint errors output

Lets examine the errors quickly. One of our default rules enforces a blank line at the end of each file. An error was thrown for index.ts saying we need a blank line. We can also see an error where we used double quotes instead of single quotes in our sum.test.ts as well as some missing semi-colons. Now we could manually go through and correct all these errors, or we can simply just run our lint-fix command. Lets do that.

npm run lint-fix

And if we run our lint command again, npm run lint, we’ll see all of our errors have been corrected and our code is formatted according to our eslint/prettier rules.

 

Git Hooks With Husky & Lint Staged

Lets take a look at how we can tie into git hooks with husky to run scripts pre and post git commands. We will setup husky to hook into pre-commit in order to run our linter before we allow a commit to be committed. We will also hook into pre-push to run our tests before we allow a push to a remote repository. Doing this will enforce our linting rules as well as ensure the code we are pushing doesn’t cause test breakage.

We can install husky as a dev dependency. We are also going to install a package called lint-staged. The lint-staged package will let us run our linter only on files added to the git staging area rather than our whole project.

npm install --save-dev husky lint-staged

Lets update our package.json file with our husky and lint-stage configurations. We will need to add two new properties, husky and lint-staged, at the root level of the object.

{
  ...
  "lint-staged": {
    "*.ts": "npm run lint"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "pre-push": "npm test"
    }
  }
  ...
}

Lets make some changes to our src/index.ts to test our git hooks. We will update our index file to calculate the sum before returing the result. We will intentionally make a formatting error to error out on pre-commit. Update your src/index.ts.

export const sum: Function = (a: number, b: number): number => {
  const result: number = a + b // <== Leave out the semicolon to trigger error on commit.
  return result;
};

Now if we stage and commit our change, we should receive an error on the commit.

git add src/index.ts
git commit -m 'calculate result before returning'

Git hook triggered output

We can manually fix our errors or we can run npm run lint-fix. Since changes were made to our staged files by fixing our errors, we will have to restage our files then commit again.

npm run lint-fix
git add src/index.ts
git commit -m 'calculate result before returning';

Another option we have is to auto fix the errors on commit instead of erroring out. We would simply update our package.json file and change the lint-staged property to run npm run lint-fix instead.

{
  ...
  "lint-staged": {
    "*.ts": "npm run lint-fix"
  },
  ...
}

Remember to stage and commit all our changes to your repostiory.

git add .
git commit -m 'add eslint, prettier, husky, and lint-staged'

 

The completed github repository can be found here

 

 

comments powered by Disqus