Search code examples
angularmockingangular-cli

How not to include mock services in Angular2 production bundle


When building an angular 2 app for production we use

ng build --prod --w --aot 

But our mock services also get bundled and minified probably because we have

import {XMockService} from "./xxx-mock.service";

and that prevents the tree shaking from dropping the unused service. Here is our simplified app.module.ts which conditionally lazy-loads the mock service when environment.mock=true

I would imagine this is a common scenario but I can't find any answers for it.

app.module.ts:

import {BrowserModule} from "@angular/platform-browser";
import {NgModule} from "@angular/core";
import {FormsModule} from "@angular/forms";
import {HttpModule} from "@angular/http";
import {AppComponent} from "./app.component";
import {environment} from "../environments/environment";
import {XService} from "./xxx.service";
import {XMockService} from "./xxx-mock.service";

let importedModules: Array<any> = [
  XService
];

if (environment.mock) {
  importedModules.push(
    {provide: XService, useClass: XMockService}
  );
} else {
  importedModules.push(
    XService
  );
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: importedModules,
  bootstrap: [AppComponent]
})

export class AppModule {}

Solution

  • The chosen answer no longer works. For Angular CLI 6, as recommended, we'll have to treat dev & prod as separate projects sharing the same codebase.

    Solution summary

    • Have separate projects for each env. (As simple as a few lines in angular.json)
    • Have as many AppModules as the number of envs with separate main.ts files for each
    • Have separate tsconfig files for each env
    • Tell angular about the changes through angular.json

    Details

    Make a copy of AppModule and name it DevModule. (DevModule for dev & AppModule for prod). DevModule will be the one with the mock services.

    Have separate main.ts files for each. Say prod-main.ts for prod & dev-main.ts for dev. The only difference being that the prod-main loads AppModule while dev-main loads DevModule.

    Duplicate the tsconfig.app.json file into tsconfig.prod.json & tsconfig.dev.json. In the exclude section of tsconfig.prod.json, type in "dev-main.ts", "app/dev-module.ts", "**/mock-*.ts". Type in the other dev modules too (the ones that load the mocks).

    Repeat for dev. For tsconfig.dev.json, exclude prod-main.ts, app/app-module.ts. Make relevant changes to tsconfig.spec.json files too, if necessary (with similar excludes).

    Update angular.json files to reflect the new tsconfig files.

    // angular.json. Only the relevant parts are given below.
    // Essentially, separate tsconfig files for 2 projects - dev & prod
    {
      "newProjectRoot": "projects",
      "projects": {
        "my-prod-proj": {
          "architect": {
            "build": {
              "options": {
                "main": "src/main-prod.ts",
                "tsConfig": "src/tsconfig.app.json",
              },
            },
            "test": {
              "options": {
                "tsConfig": "src/tsconfig.prod.spec.json",
              }
            },
            "lint": {
              "options": {
                "tsConfig": [
                  "src/tsconfig.prod.json",
                  "src/tsconfig.prod.spec.json"
                ],
              }
            }
          }
        },
        "my-dev-proj": {
          "architect": {
            "build": {
              "options": {
                "main": "src/dev-main.ts",
                "tsConfig": "src/tsconfig.dev.json",
              }
            },
            "test": {
              "options": {
                "tsConfig": "src/tsconfig.dev.spec.json",
              }
            },
            "lint": {
              "options": {
                "tsConfig": [
                  "src/tsconfig.dev.json",
                  "src/tsconfig.dev.spec.json"
                ],
              }
            }
          }
        },
      },
      "defaultProject": "my-prod-proj",
    }
    

    Run the projects by name

    ng serve my-dev-proj // dev with mocks
    ng serve             // prod (set as default in angular.json)
    same goes for the build command