Dev code not being stripped in prod builds

🐞 Bug report

Command (mark with an x)

  • new
  • build
  • serve
  • test
  • e2e
  • generate
  • add
  • update
  • lint
  • xi18n
  • run
  • config
  • help
  • version
  • doc

Is this a regression?

Problem occurs in at least Angular 9 and 10.

Description

This issue is a follow-up to this Stack Overflow question. The question received some upvotes but no answers to the problem, so I’m asking here because I’m really not sure what to do.

The short version is that code gated by compile-time constants like below is not being stripped in prod builds in my project, and I have no idea what to look at to figure out why. I don’t know if it’s a bug in Angular or a problem in my code. How can I figure out the issue? Any help that can point me in the right direction would be tremendously appreciated.

I’m using this code in main.ts (just for testing, the actual code I need stripped is in a component):

if (environment.production) {
  console.warn('this is a prod build');
  enableProdMode();
}

if (!environment.production) {
  console.warn('this is a dev build');
}

And this code is output as the result of the prod build:

s.X.production&&(console.warn("this is a prod build"),Object(i.R)()),s.X.production||console.warn("this is a dev build")

Other code:

environment.prod.ts

environment.ts is exactly the same, just with false instead of true.

export const environment = {
  production: true
};

export * from './services/services';
Relevant part of angular.json

"my-prod-config": {
  "optimization": true,
  "outputHashing": "all",
  "sourceMap": false,
  "extractCss": true,
  "namedChunks": false,
  "aot": true,
  "extractLicenses": true,
  "vendorChunk": false,
  "buildOptimizer": true,
  "stylePreprocessorOptions": {
    "includePaths": [
      "src/styles"
    ]
  },
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.prod.ts"
    }
  ],
  "baseHref": "./"
}
tsconfig.base.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "downlevelIteration": true,
    "importHelpers": true,
    "module": "es2020",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "baseUrl": "src/",
    "experimentalDecorators": true,
    "allowJs": true,
    "target": "es2015",
    "lib": [
      "es2018",
      "dom"
    ],
    "paths": {
      "path1": [
        "app/modules/stripped-from-stack-overflow-example1"
      ],
      "path2": [
        "app/modules/stripped-from-stack-overflow-example2"
      ]
    }
  },
  "files": [
    "src/main.ts",
    "src/polyfills.ts"
  ],
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictTemplates": true,
    "strictInjectionParameters": true
  }
}
package.json

{
  "name": "my-app",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "section stripped": "section stripped"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "10.0.8",
    "@angular/common": "10.0.8",
    "@angular/compiler": "10.0.8",
    "@angular/core": "10.0.8",
    "@angular/forms": "10.0.8",
    "@angular/platform-browser": "10.0.8",
    "@angular/platform-browser-dynamic": "10.0.8",
    "@angular/router": "10.0.8",
    "@ng-idle/core": "9.0.0-beta.1",
    "@ng-idle/keepalive": "9.0.0-beta.1",
    "@ngneat/until-destroy": "8.0.1",
    "angular-svg-icon": "10.0.0",
    "brace": "0.11.1",
    "caniuse-lite": "1.0.30001111",
    "core-js": "3.6.5",
    "css-vars-ponyfill": "2.3.2",
    "detect-browser": "5.1.1",
    "element-closest-polyfill": "1.0.2",
    "fomantic-ui": "2.8.6",
    "moment": "2.24.0",
    "ngx-drag-drop": "2.0.0",
    "rxjs": "6.6.2",
    "tslib": "2.0.0",
    "typeface-roboto": "0.0.75",
    "uuid": "8.3.0",
    "zone.js": "0.10.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "0.1000.5",
    "@angular/cli": "10.0.5",
    "@angular/compiler-cli": "10.0.8",
    "@angular/language-service": "10.0.8",
    "@types/uuid": "8.0.1",
    "codelyzer": "6.0.0",
    "rimraf": "3.0.2",
    "rxjs-tslint-rules": "4.34.0",
    "ts-node": "8.10.2",
    "tslint": "6.1.3",
    "tslint-angular": "3.0.2",
    "typescript": "3.9.7",
    "webpack-bundle-analyzer": "3.8.0"
  }
}

🔬 Minimal Reproduction

I am actively trying to come up with a reproduction right now, but it’s difficult because I’m just guessing at what things to try. This exact setup in a new app generated with ng new does work, the dev code is stripped.

🔥 Exception or Error

N/A

🌍 Your Environment

Angular CLI: 10.0.6
Node: 12.18.1
OS: win32 x64

Angular: 10.0.10
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.1000.6
@angular-devkit/build-angular     0.1000.6
@angular-devkit/build-optimizer   0.1000.6
@angular-devkit/build-webpack     0.1000.6
@angular-devkit/core              10.0.6
@angular-devkit/schematics        10.0.6
@angular/cli                      10.0.6
@ngtools/webpack                  10.0.6
@schematics/angular               10.0.6
@schematics/update                0.1000.6
rxjs                              6.6.2
typescript                        3.9.7
webpack                           4.43.0

Anything else relevant?

  • If it helps, something seems to be very wrong with optimization. Here’s a screenshot of our bundle stats. The largest index.ts there has a parsed size of 373 KB, but it contains nothing other than interfaces, several enums, and several const enums. They are small enums and there are only a few, the vast majority of things in this barrel are interfaces (and none of the interfaces reference classes of any kind). This exact same project using Angular 9 instead of 10 makes that barrel 20 KB instead of 373 KB.
    • I think this is probably a separate issue because it’s only a problem in Angular 10, whereas dev code isn’t stripped in 9 or 10, but I’m mentioning it just in case.
  • Code bounded with if (false) { } is properly stripped.
  • Removing the services export from the end of environment{.prod}.ts does not fix the problem.
  • All CommonJS dependencies were removed, the CLI no longer complains about them, but the problem persists.

1 possible answer(s) on “Dev code not being stripped in prod builds

  1. I see a similar issue in our project. I’m not sure if it only started after our migrating to Angular 10 though – haven’t payed much attention before. We’re currently on 10.0.3 version of angular-cli.

    I was not able to make minimal reproduction.
    What I’ve learned so far is that in our project this environment file is imported in a couple of places. And only one of them seems to have any effect on the issue: removing this import from that shared service resolves the issue.
    I’ll post here if I learn anything else.