The Angular MonoRepo (I): configurations

WARNING (20200213): this is a legacy article written for Angular 7.

In the last 2 years Angular has become a very mature and performant technology widely used by many enterprises in order to approach projects with complex Front End infrastructures.

Sometimes such projects contain not only one application but several and in most of the cases those apps share large amounts of code. As a matter of fact sometimes we do not even know how many applications sharing code are going to come up in the future so the most prudent thing to do is to be somehow prepared for that.

This article is the first one of an opinionated series on how to best build up an scalable and most modern Angular multi-project repository also known as mono-repo. We will start from zero and provide our own configurations without using tools like Nx Workspaces. The series will collect a bunch tips not only regarding scaffolding but also things like Angular component libraries, NGRX, Jest testing, schematics, styling, deploying on CI or familiar boilerplate such as unsubscribing from observables. So let’s start.

First app

Creating a new Angular app from scratch is as easy as running ng new repo-name with the Angular CLI. Do not name the repository after the application since it may contain more than one of them. And try to avoid boring naming in your projects. Of course in the examples we will use generic names such as “first-app” but please choose cooler ones for your apps and libraries, something catchy and directly bound to your company’s branding identity. In the beginning this does not strike as a real issue but believe me: bad naming leads to disaster.

cd repo-name
ng g app first-app -p fst --routing --style scss --skip-tests

Check the CLI docs if you do not understand the passed options.

Now, open up the local repo in your favourite IDE and remove e2e, src, projects/first-app-e2e, projects/first-app/karma.conf.js and projects/first-app/src/test.ts. As you might have guessed we will not be using the standard testing tools.

Brilliant. Now we can start modifying and creating configuration files.

angular.json

Remove “repo-name”, “repo-name-e2e” and “first-app-e2e” projects and projects.first-app.architect.test. Also assign “first-app” to “defaultProject”. projects.first-app.architect.build.options needs to be modified, too:

{
    "outputPath": "dist/first-app",
    "index": "projects/first-app/src/index.html",
    "main": "projects/first-app/src/main.ts",
    "polyfills": "projects/first-app/src/polyfills.ts",
    "stylePreprocessorOptions": {
    "includePaths": ["projects/first-app/src/scss"]
},
"tsConfig": "projects/first-app/tsconfig.app.json",
"assets": [
    "projects/first-app/src/favicon.ico",
    "projects/first-app/src/assets",
    {
        "glob": "config.json",
        "input": "projects/first-app",
        "output": "/"
    }
],
"styles": ["projects/first-app/src/styles.scss"],
"scripts": []
},

Here we are welcoming a new folder for primary Sass sheets (reset, mixins, variables, etc.) and a config file that will take care of (mostly) our API configurations on each environment. We will talk about all of it in upcoming articles but you can already add config.json to projects/first-app.

package.json

Remove scripts.e2e and assign “jest” to scripts.test. The specific configurations and procedures on how to perform unit testing with Jest will be explained in a separate article so we will postpone the implementation of testing configs for now. However there are some dependencies that we can and should be installed now:

Yes, you guessed it: we are going to use NGRX to control the state of our application. And you guessed it, too: it will be explained in a separate article.

The dependency with Angular CDK means that we will build an style-agnostic component library in the same repository by extending the capabilities of this package. We will never use Angular Material. We will build our own Angular Material.

As per the devDependencies we need to remove all jasmine (also the @types/*), karma and protractor ones and add these:

  • @ngrx/schematics”

  • @nrwl/schematics”

  • “husky”

  • “ngrx-store-freeze”

  • “prettier”

  • “pretty-quick”

Yes, you guessed it again: we are also going to create our own schematics library by extending the NGRX ones.

Now, the name “nrwl” references a company called Narwhal Technologies. They are pretty well know for being the creators of Nx Workspaces, a wonderful tool that we will not use here because we do not actually need it. We will talk about this in greater detail. However we will install their schematics package anyway since it contains a script that can detect what projects have been affected by your changes and consequently build only those ones in CI. This is awesome and pretty handy when using the mono-repo approach.

The other referred dev dependencies make this configuration possible in our package.json:

"husky": {
    "hooks": {
        "pre-commit": "pretty-quick --staged && npm run lint",
        "pre-push": "npm test"
    }
},

Which means that we will always check on the format and syntax of our code before committing it and that we will run all the tests before pushing it. If anything goes wrong these hooks will not allow us to perform either one of those operations.

.prettierrc and .prettierignore

As well as Jest, Prettier is also a Facebook technology. It is great to unify our code format and it uses a configuration file called .prettierrc. You can use these options:

{
    "printWidth": 100,
    "singleQuote": true,
    "useTabs": false,
    "tabWidth": 2,
    "semi": true,
    "bracketSpacing": true
}

You have to add a Prettier extension to your IDE and configure it to format your code whenever you save your working file. This way your code will always look nice and clean (and pretty) and not only yours but also the code of any contributor of your team.

Optionally you can also use .prettierignore not to apply formatting to all sorts of files. E.g. you can ignore HTML files by adding this: *.html

tslint.json

In order to avoid collisions between the formatter and the linter we need to remove these rules from the latter:

  • “comment-format”

  • “curly”

  • “eofline”

  • “import-spacing”

  • “indent”

  • “max-line-length”

  • “no-trailing-whitespace”

  • “one-line”

  • “quotemark”

  • “semicolon”

  • “typedef-whitespace”

  • “angular-whitespace”

tsconfig.json and tsconfig.app.json

Our app folder structure is going to be extremely consistent and Typescript mapped paths are going to play an important role into it. We add them to the “compilerOptions” of our tsconfig.json:

"paths": {
    "@first-app-core/*": ["./projects/first-app/src/app/_core/*"],
    "@first-app-shared/*": ["./projects/first-app/src/app/_shared/*"],
    "@first-app/*": ["./projects/first-app/src/app/app/*"],
    "@first-app-feature1/*": ["./projects/first-app/src/app/feature1/*"],
    "@first-app-environments/*": ["./projects/first-app/src/environments/*"],
    "@project-scope/ng-kit": ["dist/@project-scope/ng-kit"],
    "@project-scope/test-kit": ["dist/@project-scope/test-kit"]
}

On the other hand our projects/first-app/tsconfig.app.json has to be completely modified:

{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
        "baseUrl": ".",
        "outDir": "../../out-tsc/app",
        "paths": {
            "@first-app-core/*": ["./src/app/_core/*"],
            "@first-app-shared/*": ["./src/app/_shared/*"],
            "@first-app/*": ["./src/app/app/*"],
            "@first-app-feature1/*": ["./src/app/feature1/*"],
            "@first-app-environments/*": ["./src/environments/*"],
            "@project-scope/ng-kit": ["../../dist/@project-scope/ng-kit"],
            "@project-scope/test-kit": ["../../dist/@project-scope/test-kit"]
        }
    },
    "angularCompilerOptions": {
        "preserveWhitespaces": "off"
    },
    "exclude": ["**/*.spec.ts"]
}

As well as “first-app” must be replaced with the actual name of your first application the string “feature1” must match the name of your first feature. I know: you do not have one yet. But this mapping configuration is telling you already what is coming: our apps are going to be structured in features.

In the next article we will cover the folder structure topic in detail.