top of page

Adding ESLint to an Angular Micro Frontend architecture

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, TSLint is being deprecated in favor of ESLint. 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


2. Add Super-Linter to GitHub Actions

Setting up 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.

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 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(❤) 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

{
  "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

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.

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.

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/ 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.



Source: Medium - Petar Garžina


The Tech Platform

0 comments
bottom of page