Automate Code Review for a TypeScript React project - Part 1

This solution will enforce standardization and good code quality for your entire project without any scope for compromise for anyone. It will also serve as the prefect Dev IDE setup where a developer doesn’t have to learn anything new to follow the rule set, the IDE automatically fixes everything for him.

Scope

We will be focussing on the following topics for enforcing standards

  1. IDE Tooling and Settings
  2. Code Aesthetics
  3. Code Quality
  4. Code Coverage
  5. PR Template

Setup

IDE

This article will assume you are using VS Code, but similar alternatives can be found for extensions in the IDE of your choice.

Configure VS Code using the following steps:

  • Create a folder at the root of your project called .vscode
  • Create the following two files within it

    • extensions.json

      These extensions provide a lot of options and standardized intelligent tools for the developers. The Developer will be prompted to install these on opening the project, if not already done and should be a single click install. These will also be visible under the extensions section of the IDE.

      {
        "recommendations": [
          "aaron-bond.better-comments",
          "CoenraadS.bracket-pair-colorizer-2",
          "Orta.vscode-jest",
          "PKief.material-icon-theme",
          "dbaeumer.vscode-eslint",
          "eamodio.gitlens",
          "eg2.vscode-npm-script",
          "esbenp.prettier-vscode",
          "jpoissonnier.vscode-styled-components",
          "msjsdiag.debugger-for-chrome",
          "streetsidesoftware.code-spell-checker",
          "stylelint.vscode-stylelint"
        ]
      }
      
    • settings.json

      These are IDE settings which will set specific tools and formatting options in the IDE

      {
        "[html]": {
          "editor.defaultFormatter": "esbenp.prettier-vscode"
        },
        "[javascript]": {
          "editor.defaultFormatter": "esbenp.prettier-vscode"
        },
        "[json]": {
          "editor.defaultFormatter": "esbenp.prettier-vscode"
        },
        "[typescript]": {
          "editor.defaultFormatter": "esbenp.prettier-vscode",
          "editor.tabSize": 2
        },
        "[typescriptreact]": {
          "editor.defaultFormatter": "esbenp.prettier-vscode"
        },
        "[yaml]": {
          "editor.defaultFormatter": "esbenp.prettier-vscode"
        },
        "better-comments.multilineComments": true,
        "editor.formatOnSave": true,
        "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
        "javascript.updateImportsOnFileMove.enabled": "always",
        "jest.autoEnable": false,
        "jest.runAllTestsFirst": false,
        "jest.showCoverageOnLoad": true,
        "typescript.referencesCodeLens.enabled": true,
        "typescript.tsdk": "node_modules/typescript/lib",
        "typescript.updateImportsOnFileMove.enabled": "always",
        "vsicons.projectDetection.autoReload": true,
        "workbench.iconTheme": "material-icon-theme"
      }
      

Spell Checker

This extension make sure you use correct spelling in your code for variables and comments.

  • Install Code Spell Checker extension in the IDE
  • Then create cSpell.json in .vscode folder. This file serves as the configuration for the extension
  • Addd the following code in the file

    {
      "version": "0.1",
      "words": ["TheNikhilK"]
    }
    

Prettier

Prettier is a well known, industry wide used code formatter. We will use this tool two fold in our IDE, as an extension and as a CLI Tool

  • Install the VS Code extension from this link
  • Install the tool package as a dev dependency

    npm install --save-dev --save-exact prettier
    
    ## OR
    
    yarn add --dev --exact prettier
    
    
  • Create a file at the root of your project .prettierrc, add the following configuration settings to it:

    {
      "printWidth": 100,
      "tabWidth": 2,
      "semi": true,
      "singleQuote": true,
      "trailingComma": "all",
      "arrowParens": "avoid",
      "endOfLine": "auto",
      "jsxBracketSameLine": true,
      "bracketSpacing": true
    }
    
  • Create .prettierignore for all the files and path to ignore for formatting

    dist/
    out/
    public/
    strings/
    tests/
    node_modules/
    package-lock.json
    

ESLint

  • ESLint is an advanced linter which can scan for issues as well as fix them.
  • Along with enforcing scripting rules we will also integrate Prettier rules here to make sure there are no code formatting issues and they are also flagged as violations
  • Install this extension
  • Install the following packages for CLI tools

    npm install --save-dev eslint eslint-config-prettier eslint-loader eslint-plugin-import eslint-plugin-jest eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint
    
    # OR
    
    yarn add --dev eslint eslint-config-prettier eslint-loader eslint-plugin-import eslint-plugin-jest eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint
    
  • Create .eslintrc.jsat the root of the project and add the following code

    const fs = require('fs');
    const path = require('path');
    
    const prettierOptions = JSON.parse(fs.readFileSync(path.resolve(__dirname, '.prettierrc'), 'utf8'));
    
    module.exports = {
      parser: '@typescript-eslint/parser',
      extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
        'plugin:prettier/recommended',
        'prettier/@typescript-eslint',
        'prettier/react',
        'plugin:react/recommended',
        'plugin:react-hooks/recommended',
        'plugin:jsx-a11y/recommended',
        'plugin:jest/recommended',
        'plugin:import/errors',
        'plugin:import/warnings',
        'plugin:import/typescript',
      ],
      env: {
        browser: true,
        es6: true,
        jest: true,
        amd: true,
        node: true,
      },
      plugins: [ 'prettier', 'jest', 'jsx-a11y', 'import', 'react'],
      rules: {
        'prettier/prettier': ['error', prettierOptions],
        'react/no-multi-comp': [2, { ignoreStateless: false }],
        'react/display-name': 0,
        'react/jsx-pascal-case': 2,
        'react/prefer-read-only-props': 2,
        'react/sort-prop-types': 2,
        'react/jsx-sort-props': 2,
        'react/jsx-max-depth': [1, { max: 4 }],
        'react/jsx-no-literals': 2,
        'react/no-typos': 2,
        'react/no-multi-comp': 2,
        'react/no-direct-mutation-state': 2,
        'react/jsx-no-duplicate-props': 2,
        'react/prefer-es6-class': 2,
        'react/boolean-prop-naming': 2,
        'react/function-component-definition': 2,
        'react/sort-comp': [
          2,
          {
            order: ['instance-variables', 'static-methods', 'lifecycle', '/^on.+$/', 'everything-else', 'render'],
          },
        ],
        '@typescript-eslint/interface-name-prefix': 0,
        '@typescript-eslint/no-parameter-properties': 0,
        '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
        '@typescript-eslint/explicit-function-return-type': 2,
        '@typescript-eslint/switch-exhaustiveness-check': 2,
        '@typescript-eslint/prefer-optional-chain': 1,
        '@typescript-eslint/prefer-nullish-coalescing': 1,
        '@typescript-eslint/no-unnecessary-qualifier': 1,
        '@typescript-eslint/no-unsafe-return': 2,
        '@typescript-eslint/no-unsafe-call': 2,
        '@typescript-eslint/consistent-type-assertions': 2,
        '@typescript-eslint/consistent-type-definitions': 2,
        '@typescript-eslint/no-base-to-string': 2,
        '@typescript-eslint/prefer-ts-expect-error': 2,
        '@typescript-eslint/prefer-readonly': 2,
        '@typescript-eslint/prefer-readonly-parameter-types': 2,
        '@typescript-eslint/explicit-module-boundary-types': 2,
        '@typescript-eslint/prefer-nullish-coalescing': 2,
        '@typescript-eslint/naming-convention': [2, { selector: 'variable', format: ['camelCase', 'PascalCase'] }],
        '@typescript-eslint/interface-name-prefix': [2, { prefixWithI: 'always' }],
        'no-use-before-define': 2,
        'no-var': 2,
        eqeqeq: 2,
        'prefer-const': 2,
        'prefer-template': 2,
        'prefer-spread': 2,
        'spaced-comment': 1,
        'max-lines': [2, { max: 400, skipComments: true }],
        'max-lines-per-function': [2, { max: 75, skipComments: true }],
        'max-statements-per-line': 2,
        'multiline-comment-style': 2,
        'no-shadow': 2,
        'no-restricted-globals': [
          2,
          {
            name: 'fdescribe',
            message: 'Do not commit fdescribe. Use describe instead.',
          },
        ],
        'import/newline-after-import': [2, { count: 1 }],
        'import/no-duplicates': 2,
        'import/no-unresolved': 0,
        'import/named': 0,
        'import/no-named-as-default': 0,
        'padding-line-between-statements': [
          2,
          {
            blankLine: 'always',
            prev: '*',
            next: ['return', 'class', 'try', 'for', 'if', 'switch', 'while'],
          },
        ],
        'lines-between-class-members': [2, 'always'],
      },
      parserOptions: {
        sourceType: 'module',
        project: './tsconfig.json',
        ecmaFeatures: {
          jsx: true,
        },
      },
      settings: {
        react: {
          pragma: 'React',
          version: 'detect',
        },
      },
    };
    
  • Create .eslintignoreat the root level and add files or paths which you don’t want to scan for ESLint

      .vscode
      configs
      dist
      node_modules
      out
      public
    

More details on how to configure other settings can be found in Part 2 of this guide.