2

After upgrading to Angular 8, ng build generates a proper index.html that supports differential loading however ng serve generates a different index.html that contains references to only some of the generated 'es5' scripts. As a result, the app does not work in ES5 browsers like IE 11.

So far I have:

  • confirmed the app runs as expected in modern ES2015+ browsers like Chrome
  • confirmed the app worked on IE 11 prior to the Angular 8 upgrade
  • compared index.html source in IE 11 to source generated in dist directory from ng build, can see differences in script references (see sample code below)
  • searched angular and angular-cli issues on Github and stackoverflow for similar problems
  • tried building and serving with and without the soon to be deprecated es5BrowserSupport option in angular.json
  • updated to the latest versions of @angular-devkit and @angular packages

The repository I am working on contains an application project and a library project. Here are the relevant architect targets from angular.json after the Angular 8 upgrade:

      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/ng-shared-components-tester",
            "index": "projects/ng-shared-components-tester/src/index.html",
            "main": "projects/ng-shared-components-tester/src/main.ts",
            "polyfills": "projects/ng-shared-components-tester/src/polyfills.ts",
            "tsConfig": "projects/ng-shared-components-tester/tsconfig.app.json",
            "assets": [
              "projects/ng-shared-components-tester/src/favicon.ico",
              "projects/ng-shared-components-tester/src/assets"
            ],
            "styles": [
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "projects/ng-shared-components-tester/src/styles.css",
              "node_modules/abc-emerald-standards/dist/emerald/assets/css/default.css",
              "node_modules/abc-emerald-standards/dist/emerald/assets/css/forms.css"
            ],
            "scripts": [],
            "es5BrowserSupport": true
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "projects/ng-shared-components-tester/src/environments/environment.ts",
                  "with": "projects/ng-shared-components-tester/src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "ng-shared-components-tester:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "ng-shared-components-tester:build:production"
            }
          }
        },

Here is the browserlist file with IE 11 support enabled:

> 0.5%
last 4 versions
IE 11
not dead

tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "importHelpers": true,
    "outDir": "./dist/out-tsc",
    "baseUrl": "./",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "module": "esnext",
    "paths": {
      "@abc/ng-shared-components": [
        "dist/abc/ng-shared-components"
      ],
      "@abc/ng-shared-components/*": [
        "dist/abc/ng-shared-components/*"
      ]
    }
  },
  "angularCompilerOptions": {
    "annotateForClosureCompiler": true,
    "skipTemplateCodegen": true,
    "strictMetadataEmit": true,
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  },
}

Expected Result

ng build is generated the expected result in dist/ng-shared-components-tester/index.html:

<body>
  <app-root></app-root>
  <script src="runtime-es2015.js" type="module"></script>
  <script src="polyfills-es2015.js" type="module"></script>
  <script src="runtime-es5.js" nomodule></script>
  <script src="polyfills-es5.js" nomodule></script>
  <script src="styles-es2015.js" type="module"></script>
  <script src="styles-es5.js" nomodule></script>
  <script src="vendor-es2015.js" type="module"></script>
  <script src="main-es2015.js" type="module"></script>
  <script src="vendor-es5.js" nomodule></script>
  <script src="main-es5.js" nomodule></script>
</body>

Actual Result

ng serve is serving up an index.html to IE 11 that only contains the ES5 version of pollfills.js:

<body>
  <app-root></app-root>
  <script src="runtime.js"></script>
  <script src="polyfills-es5.js" nomodule></script>
  <script src="polyfills.js"></script><script src="styles.js"></script>
  <script src="vendor.js"></script>
  <script src="main.js"></script>
</body>

2 Answers 2

3

After a little more digging I found this discussion on GitHub that indicates differential loading is disabled by default for ng serve and ng build --watch by design. This appears to be done for performance reasons (differential loading requires 2 builds to run, ES5 and non-ES5). There is a workaround to use ES5 bundles instead of the default bundles.

Sign up to request clarification or add additional context in comments.

Comments

1

That is the correct behavior of your code .Angular CLi will detect the browser and inject the polyfill script at the runtime like this

enter image description here

So if you are using older browser version you will the script that have nomodule tag

You can read it more here

2 Comments

I agree that polyfills-es5.js with a nomodule tag is correct behavior. However, what about runtime-es5.js, styles-es5.js, vendor-es5.js and main-es5.js? When the app loads in the browser, it is failing because these scripts contain ES2015+ code (ex. classes) that won't work in IE 11. I was expecting es5 versions of these scripts to be injected into index.html as well when using using ng serve just like ng build.
Can you add your tsconfig please

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.