Adding ESLint to an Angular Micro Frontend architecture
Tags: angular, eslint, microfrontend
Published: 2021-05-18 13:47:21

Recently we moved our platform from a mono repo to a micro frontend architecture. One of the things we wanted to add was a base linter that is shared across our apps to ensure a more standardized code as both the platform and the number of micro applications expand.

In this post I'll share the basic rundown of the things we had to do in order to get the linter up and running.



## 1. Transition from TSLint to ESLint

As noted in the ng lint [Angular Docs] (https://angular.io/cli/lint), [TSLint](https://palantir.github.io/tslint/) is being deprecated in favor of [ESLint](https://eslint.org/). The migration is rather straightforward and boils down to these two lines:

installing the schematics

`ng add @angular-eslint/schematics`

and running the converter

`ng g @angular-eslint/schematics:convert-tslint-to-eslint`

For a more detailed migration guide, see this article: [https://dev.to/gsarciotto/migrating-and-configuring-eslint-with-angular-11-3fg1](https://dev.to/gsarciotto/migrating-and-configuring-eslint-with-angular-11-3fg1)

## 2. Add Super-Linter to GitHub Actions

Setting up [Super-Linter](https://github.com/github/super-linter) was super easy, since we already had a workflows `test-pull-req.yml` file that tests our build on each pull request. Adding the linter was merely adding another step to the process.

```yml

name: Test And Lint Pull Request
on:
  pull_request:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
...
      - name: Lint Code Base
        uses: github/super-linter@v3
        env:
          NODE_ENV: production
          VALIDATE_ALL_CODEBASE: false
          DEFAULT_BRANCH: main
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          LINTER_RULES_PATH: /
          TYPESCRIPT_ES_CONFIG_FILE: .eslintrc.js
          VALIDATE_TYPESCRIPT_ES: true
...
```

The Super-Linter [docs](https://github.com/github/super-linter) are pretty self explanatory, the only additional thing we added here is the `NODE_ENV` variable, that will be used a bit later. The `VALIDATE_ALL_CODEBASE` variable came in nicely as the linter lints only files changed, so it makes adding new linter rules a bit more easier.

At this point you are good to go, you have migrated to the new ESLint and your files are being linted on each Pull Request. Tap yourself on the back!

## 3. Share and extend the base lint rules
Since we have around 10 applications, adding or changing a rule requires us to change it in each of the 10 applications; ain't nobody got time for that!

When we switched to the micro frontend platform we started utilizing our own Angular library for some of the configs, components, pipes and services that we use around the platform. We also keep a part of our [Tailwind](https://tailwindcss.com/)(❤) config in our libraries `assets` folder, so that was the obvious place to put our base lint config as well.

One thing to note here is, to make your library include the `assets` folder in the final `dist` you have to explicitly tell it to.

This happens in the libraries `ng-package.json`

```json
{
  "dest": "../../dist/app-library",
  "assets": ["./assets"],
  "lib": {
    "entryFile": "src/public-api.ts"
  }
}
```

Now each application's `eslintrc.js` can reference and extend the `base-eslint.js` using the `extends` property


```javascript
module.exports = {
 root: true,
 extends: [
   "./node_modules/@cognism/app-library/assets/configs/linter/base-eslint.js"
 ],
 ...
}
```

## 4. Add application specific rules
This is how our most basic `eslintrc.js` config looks like in one of our micro applications.

```javascript
module.exports = {
 root: true,
 extends: [
   "./node_modules/@cognism/app-library/assets/configs/linter/base-eslint.js"
 ],
 overrides: [
   {
     files: ["*.ts"],
     rules: {
       "@angular-eslint/component-selector": [
         "error",
         {
           type: "element",
           prefix: "app-nav",
           style: "kebab-case"
         }
       ],
       "@angular-eslint/directive-selector": [
         "error",
         {
           type: "attribute",
           prefix: "appNav",
           style: "camelCase"
         }
       ]
     }
   }
 ]
}
```

As you can see, first we extend our `base-eslint.js` rules and then we override it with our application specific rules. In this case we just want to have application specific prefixes for both components and directives.

## 5. Add environment dependent rules
The idea here was to enable different rule behaviors depending on the environment. For example the `console.log`. We don't want the log statement committed to the main branch, but we also don't want to give out errors to developers while writing logs in their local environment.

The easiest way to do it was by simply using a ternary operator inside the lint file. Note that your config file must be in `.js` format and not in the default `.json` format to be able to do this.

```javascript
module.exports = {
  ...
      rules: {
        "@typescript-eslint/naming-convention": [
          "error",
          { "selector": "enumMember", "format": ["UPPER_CASE"] }
        ],
        "@angular-eslint/no-empty-lifecycle-method": "off",
        "no-console": process.env.NODE_ENV === 'production' ? "error" : 'warn'
      }
   ...
}
```

As you can see, this is where the `NODE_ENV` kicks in which we defined in our GitHub Actions `test-pull-req.yml` file.

We did implement different environments this way, but are also aware that it might get messy with a lot of ternaries in different rules. If that comes to be the case, we ll just start using two files, eg. `eslintrc.js` and `prod-eslintrc.js` and the `test-pull-req.yml` file will always point to `prod-eslintrc.js` while in development we'll use `eslintrc.js`.

## 6. There you have it!
- We used single-spa [https://single-spa.js.org/](https://single-spa.js.org/) to move our monolith to the front-end microservices world.
- The Angular version used was v11.
- If you need any additional info feel free to reach out.
- Any comments and improvements are welcome.

***

Feel free to connect 👋

[Twitter](https://twitter.com/pgarzina) | [Instagram](https://www.instagram.com/pgarzina/) | [LinkedIn](https://www.linkedin.com/in/petar-garzina/)