Configuring ESLint for AWS-CDK on VSCode
Tags: typescript, awscdk, vscode, eslint
Published: 2024-04-23 10:55:29

If you, like me, landed on AWS-CDK from the flying world of Terraform and has a good taste for well-indented files, one of the first things you will notice is that for working with TypeScript, keeping your files looking clean and beauty is not as simple as installing a single VSCode plugin.
Wanna change that? Follow along.

## Assumptions
The guide below assumes that you have already:
* VSCode installed
* Node installed
* NPM installed
* A CDK TypeScript project bootstraped

## Project dependencies

Install ESLint packages

```bash
npm install eslint --save-dev
```

Then, run the init config for ESLint:

```bash
npx eslint --init 
```

It will ask you a couple of questions (pay attention to the browser/node one, as this allows you to pick both), ask you to install some dependencies, which you can confirm and finally genereate a `.eslintrc.json` (if you picked JSON for config format, like me).

![ESLint Init Config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqrn9ds3vcnp7dgqq27e.png)

The `.eslintrc.json` file can be adjusted later.

## VSCode Extension

In VSCode Extensions marketplace, install the ESLint extension:

![ESLint extension](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6nftiwxtfu40jss2agh.png)

## Customizing ESLint config

It takes some effort to put all the available settings of ESLint in place and get a decent result. You can check 
[eslint-config-love](https://github.com/mightyiam/eslint-config-love) for detailed instructions on how to create settings that match your needs.

Or you can make use of the example below that has already some formatting standards setup and apply your changes as you identify them. Just save the content of this file into your `.eslintrc.json` and see what happens.

**Important note:** as my CDK code usually stays on "infrastrusture" folder, extra parameter `tsconfigRootDir` is necessary to tell linter where to find `tsconfig.json` file. Else, linter won't be able to find the imports.

```json
{
  "env": {
    "jest": true,
    "node": true
  },
  "root": true,
  "plugins": [
    "@typescript-eslint",
    "import"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module",
    "project": "./tsconfig.json",
    "tsconfigRootDir": "./infrastructure"
  },
  "extends": [
    "plugin:import/typescript"
  ],
  "settings": {
    "import/parsers": {
      "@typescript-eslint/parser": [
        ".ts",
        ".tsx"
      ]
    },
    "import/resolver": {
      "node": {},
      "typescript": {
        "project": "./tsconfig.json",
        "alwaysTryTypes": true
      }
    }
  },
  "ignorePatterns": [
    "*.js",
    "!.projenrc.js",
    "*.d.ts",
    "node_modules/",
    "*.generated.ts",
    "coverage",
    "!.projenrc.ts",
    "!projenrc/**/*.ts"
    ],
  "rules": {
    "indent": [
      "off"
    ],
    "@typescript-eslint/indent": [
      "error",
      2
    ],
    "quotes": [
      "error",
      "single",
      {
        "avoidEscape": true
      }
    ],
    "comma-dangle": [
      "error",
      "always-multiline"
    ],
    "comma-spacing": [
      "error",
      {
        "before": false,
        "after": true
      }
    ],
    "no-multi-spaces": [
      "error",
      {
        "ignoreEOLComments": false
      }
    ],
    "array-bracket-spacing": [
      "error",
      "never"
    ],
    "array-bracket-newline": [
      "error",
      "consistent"
    ],
    "object-curly-spacing": [
      "error",
      "always"
    ],
    "object-curly-newline": [
      "error",
      {
        "multiline": true,
        "consistent": true
      }
    ],
    "object-property-newline": [
      "error",
      {
        "allowAllPropertiesOnSameLine": true
      }
    ],
    "keyword-spacing": [
      "error"
    ],
    "brace-style": [
      "error",
      "1tbs",
      {
        "allowSingleLine": true
      }
    ],
    "space-before-blocks": [
      "error"
    ],
    "curly": [
      "error",
      "multi-line",
      "consistent"
    ],
    "@typescript-eslint/member-delimiter-style": [
      "error"
    ],
    "semi": [
      "error",
      "always"
    ],
    "max-len": [
      "error",
      {
        "code": 150,
        "ignoreUrls": true,
        "ignoreStrings": true,
        "ignoreTemplateLiterals": true,
        "ignoreComments": true,
        "ignoreRegExpLiterals": true
      }
    ],
    "quote-props": [
      "error",
      "consistent-as-needed"
    ],
    "@typescript-eslint/no-require-imports": [
      "error"
    ],
    "import/no-extraneous-dependencies": [
      "error",
      {
        "devDependencies": [
          "**/test/**",
          "**/build-tools/**",
          ".projenrc.ts",
          "projenrc/**/*.ts"
        ],
        "optionalDependencies": false,
        "peerDependencies": true
      }
    ],
    "import/no-unresolved": [
      "error"
    ],
    "import/order": [
      "warn",
      {
        "groups": [
          "builtin",
          "external"
        ],
        "alphabetize": {
          "order": "asc",
          "caseInsensitive": true
        }
      }
    ],
    "no-duplicate-imports": [
      "error"
    ],
    "no-shadow": [
      "off"
    ],
    "@typescript-eslint/no-shadow": [
      "error"
    ],
    "key-spacing": [
      "error"
    ],
    "no-multiple-empty-lines": [
      "error"
    ],
    "@typescript-eslint/no-floating-promises": [
      "error"
    ],
    "no-return-await": [
      "off"
    ],
    "@typescript-eslint/return-await": [
      "error"
    ],
    "no-trailing-spaces": [
      "error"
    ],
    "dot-notation": [
      "error"
    ],
    "no-bitwise": [
      "error"
    ],
    "@typescript-eslint/member-ordering": [
      "error",
      {
        "default": [
          "public-static-field",
          "public-static-method",
          "protected-static-field",
          "protected-static-method",
          "private-static-field",
          "private-static-method",
          "field",
          "constructor",
          "method"
        ]
      }
    ]
  },
  "overrides": [
    {
      "files": [
        ".projenrc.js"
      ],
      "rules": {
        "@typescript-eslint/no-require-imports": "off",
        "import/no-extraneous-dependencies": "off"
      }
    }
  ]
}

```

PS: Sometimes changing the `.eslintrc.json` requires restarting VSCode to take effect.

## Running the linter

Include a script to run eslint in your`package.json` file:

```json
"eslint": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern lib bin test"
```

After that, you can run `npm run eslint` for a list of files that don't match the pattern you defined. Happy coding.