Can @ngtools/webpack AngularWebpackPlugin link partially compiled ivy libraries?

2.9k views Asked by At

I've got this somewhat old Angular web application which was updated from Angular 6 to Angular 12, however, Angular CLI is not being used for building it, instead it relies on Webpack 5, its loaders and AngularWebpackPlugin from @ngtools/webpack.

Recently, I needed to upgrade to a latest version of a library I was using and I got greeted with the following warning after my production build:

Uncaught Error: The injectable 'j' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.

The injectable is part of a library that has been partially compiled.
However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.

Ideally, the library is processed using the Angular Linker to become fully AOT compiled.
Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',
or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.

After removing the minimizer and looking at the error stack trace in more detail, it appears to be an issue with that new library, specifically.

Now, I did some digging and found out that, since Angular 11.1, libraries can now be partially compiled for Ivy and shipped to NPM that way, not requiring ngcc. And, as expected, that library's source code indicates that, indeed, the major difference between the older version that I was using and the newer one that I am trying to use is that the newer one is partially compiled.

Now, my application's webpack configuration is as follows (I only left settings i consider relevant, if needed I can provide the entire configuration file):

module.exports = {
...
    module: {
        rules: [
            {
                test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
                loader: '@ngtools/webpack'
            }
        ],
    },
...
    plugins: [
        new CleanWebpackPlugin(),

        new AngularWebpackPlugin({
            tsconfig: './tsconfig-aot.json'
        }),

        new HtmlWebpackPlugin({
            ...
        }),

        new CopyWebpackPlugin({
            patterns: [...]
        })
    ]
};

My question is am I missing a step here? Is there a certain plugin i need to use to "link" the partially compiled libraries so I avoid this issue? Is it even possible to compile Angular application for production using Webpack in this way? I've tried a bunch of things, but everything points out to me missing a step in this entire process. Also, I created a test application using Angular CLI and used it to install the library and compile it using production mode and it all works like a charm so I can rule out the idea of it being the library creator's fault.

App dependencies are as follows:

"dependencies": {
    "@angular/animations": "12.2.0",
    "@angular/cdk": "^12.2.0",
    "@angular/common": "12.2.0",
    "@angular/compiler": "12.2.0",
    "@angular/core": "12.2.0",
    "@angular/forms": "12.2.0",
    "@angular/platform-browser": "12.2.0",
    "@angular/platform-browser-dynamic": "12.2.0",
    "@angular/platform-server": "12.2.0",
    "@angular/router": "12.2.0",
    "@angular/upgrade": "12.2.0",
    "@fullcalendar/core": "^5.9.0",
    "angular-auth-oidc-client": "^12.0.2",
    "angular-google-tag-manager": "^1.4.2",
    "angular2-ladda": "^2.0.0",
    "angular2-uuid": "1.1.1",
    "core-js": "^3.16.1",
    "headroom.js": "^0.12.0",
    "ngx-google-places-autocomplete": "^2.0.5",
    "ngx-headroom": "^1.0.6",
    "primeicons": "^4.1.0",
    "primeng": "^11.4.5",
    "protractor": "^7.0.0",
    "rxjs": "^6.6.7",
    "tslib": "^2.3.0",
    "zone.js": "0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^12.2.0",
    "@angular/cli": "12.2.0",
    "@angular/compiler-cli": "^12.2.2",
    "@ngtools/webpack": "^12.2.2",
    "@types/core-js": "^2.5.5",
    "@types/google.maps": "^3.45.6",
    "@types/jasmine": "^3.8.2",
    "@types/node": "10.11.7",
    "angular-router-loader": "0.8.5",
    "angular2-template-loader": "0.6.2",
    "autoprefixer": "^9.8.6",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^6.4.1",
    "css-loader": "^1.0.0",
    "file-loader": "2.0.0",
    "html-webpack-plugin": "^5.3.2",
    "istanbul-instrumenter-loader": "^3.0.1",
    "jasmine": "^3.8.0",
    "jasmine-spec-reporter": "^5.0.2",
    "jasmine-ts": "^0.4.0",
    "karma": "^6.3.4",
    "karma-chrome-launcher": "^3.1.0",
    "karma-coverage": "^2.0.3",
    "karma-coverage-istanbul-reporter": "^3.0.3",
    "karma-jasmine": "^4.0.1",
    "karma-jasmine-html-reporter": "^1.7.0",
    "karma-webpack": "^5.0.0",
    "postcss-loader": "3.0.0",
    "raw-loader": "0.5.1",
    "rxjs-tslint": "^0.1.8",
    "sass": "^1.37.5",
    "sass-loader": "^10.2.0",
    "source-map-loader": "^0.2.4",
    "style-loader": "0.23.1",
    "terser-webpack-plugin": "^4.2.3",
    "ts-loader": "^8.3.0",
    "ts-node": "^8.10.2",
    "tslint": "^6.1.3",
    "tslint-loader": "3.5.4",
    "typescript": "^4.3.5",
    "webpack": "^5.51.0",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.0.0"
  }
1

There are 1 answers

1
Stevethemacguy On BEST ANSWER

Is there a certain plugin i need to use to "link" the partially compiled libraries so I avoid this issue?

Yes! You need to add the Angular Linker to process the problematic plugin. It's currently only available as a Babel plugin: @angular/compiler-cli/linker/babel

In short, add this to your Webpack config and replace ng-click-outside with your plugin(s):

rules: [
{
  // Explicit rule to run the linker over partial libraries
  test: /.*\.js$/,
  include: /node_modules\/ng-click-outside/,
  use: [
    {
      loader: 'babel-loader',
      options: {
        configFile: false,
        plugins: ['@angular/compiler-cli/linker/babel'], // Required!
      }
    }
  ]
},
{
  // Regular rule for all non-library files
  test: /\.[jt]sx?$/,
  exclude: /node_modules/,
  use: [
    {loader: 'babel-loader'}, // Optional
    {
      loader: '@ngtools/webpack', // Required
      options: {
        directTemplateLoading: false, // See https://www.npmjs.com/package/@ngtools/webpack
      }
    },
    {loader: '@angular-devkit/build-optimizer/webpack-loader'}, // Optional
  ]
},

Also, you should remove your old @ngtools/webpack rule. You no longer need to process 'ngfactory' or 'ngstyle' files in Angular 12.

For more details, see my other SO answer here. Credit for the webpack config goes to Robert van Hoesel from this Github issue.