DEV Community

Petar Garžina
Petar Garžina

Posted on • Edited on

Adding ESLint to an Angular Micro Frontend architecture

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, 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
...
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
 ],
 ...
}
Enter fullscreen mode Exit fullscreen mode

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"
         }
       ]
     }
   }
 ]
}
Enter fullscreen mode Exit fullscreen mode

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'
      }
   ...
}
Enter fullscreen mode Exit fullscreen mode

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.

Feel free to connect 👋

Twitter | Instagram | LinkedIn

Top comments (2)

Collapse
 
alxizr profile image
alxizr

Hi Petar,

i'm a bit confused and wondering why would you migrate to single-spa if you were already using Angular? Why didn't you switch to Angular workspaces ? This is the entire reason its there. Provide you with the ability to construct your monolithic SPA into smaller chunks of Angular frontends and you could also use global routing

Collapse
 
pgarzina profile image
Petar Garžina

Hey, thanks for the question and sorry for the late reply. We wanted to have the option to plug in an application written in something other than Angular, like React of Vue, and thats where single-spa plays nicely since its main router is framework agnostic.