angular-cli: Runtime errors when using buildOptimizer. JIT compilation broken.

🐞 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?

I don’t know. I would guess this is a recent thing arising from the introduction of AOT, Ivy, and the build optimizer.

Description

We faced an issue with an application we were developing where the legacy application had content stored in a database. This content is essentially an Angular template and could not be known at compile-time. The situations and constraints in the project led us to create dynamic components/modules on the fly. This worked beautifully in development and then failed when deployed to production.

In diagnosis, this works as long as the build optimizer is NOT enabled. Having a production build, with the optimizer, but without the buildOptimizer, enabled still works.

I believe I’ve seen unofficially in other issues that using AOT and JIT together, but since it works with the buildOptimizer disabled, this feels like an issue with the optimizer.

🔬 Minimal Reproduction

Here is a reproduction repo Once you’ve npm install’d, you can run the repro this way. ng serve <-- working ng serve --configuration=production <–working ng serve --configuration=production-build-optimized <-- broken. The only difference between the configurations is the buildOptimizer setting. You can run all three side-by-side-by-side by adding different --port values.

In a working build, you’ll see an additional “Runtime Hello!” message as well as the Angular logo. When broken, those elements will be missing and there will be a console error.

If this isn’t a bug, that error message is misleading for this situation… 😃

🔥 Exception or Error


main.7000374ef126bad8069c.js:1 ERROR Error: Uncaught (in promise): Error: Angular JIT compilation failed: '@angular/compiler' not loaded!
  - JIT compilation is discouraged for production use-cases! Consider AOT mode instead.
  - Did you bootstrap using '@angular/platform-browser-dynamic' or '@angular/platform-server'?
  - Alternatively provide the compiler with 'import "@angular/compiler";' before bootstrapping.
Error: Angular JIT compilation failed: '@angular/compiler' not loaded!
  - JIT compilation is discouraged for production use-cases! Consider AOT mode instead.
  - Did you bootstrap using '@angular/platform-browser-dynamic' or '@angular/platform-server'?
  - Alternatively provide the compiler with 'import "@angular/compiler";' before bootstrapping.
    at Ae (main.7000374ef126bad8069c.js:1)
    at Function.get (main.7000374ef126bad8069c.js:1)
    at St (main.7000374ef126bad8069c.js:1)
    at new Af (main.7000374ef126bad8069c.js:1)
    at ud (main.7000374ef126bad8069c.js:1)
    at dd (main.7000374ef126bad8069c.js:1)
    at e.md [as compileModuleAndAllComponentsAsync] (main.7000374ef126bad8069c.js:1)
    at e.<anonymous> (main.7000374ef126bad8069c.js:1)
    at Generator.next (<anonymous>)
    at main.7000374ef126bad8069c.js:1
    at T (polyfills.a8d522dff2c1a5c4b9db.js:1)
    at polyfills.a8d522dff2c1a5c4b9db.js:1
    at a (main.7000374ef126bad8069c.js:1)
    at l.invoke (polyfills.a8d522dff2c1a5c4b9db.js:1)
    at Object.onInvoke (main.7000374ef126bad8069c.js:1)
    at l.invoke (polyfills.a8d522dff2c1a5c4b9db.js:1)
    at i.run (polyfills.a8d522dff2c1a5c4b9db.js:1)
    at polyfills.a8d522dff2c1a5c4b9db.js:1
    at l.invokeTask (polyfills.a8d522dff2c1a5c4b9db.js:1)
    at Object.onInvokeTask (main.7000374ef126bad8069c.js:1)

🌍 Your Environment



     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 9.1.4
Node: 13.11.0
OS: darwin x64

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

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.901.4
@angular-devkit/build-angular     0.901.4
@angular-devkit/build-optimizer   0.901.4
@angular-devkit/build-webpack     0.901.4
@angular-devkit/core              9.1.4
@angular-devkit/schematics        9.1.4
@ngtools/webpack                  9.1.4
@schematics/angular               9.1.4
@schematics/update                0.901.4
rxjs                              6.5.5
typescript                        3.8.3
webpack                           4.42.0
    

Anything else relevant?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 29 (3 by maintainers)

Most upvoted comments

For everyone interested in solving this issue with webcomponents check out my 2 comments in the issue here:

https://github.com/angular/angular/issues/15275#issuecomment-703678535

You just officially stripped a feature out of Angular that set it apart from other Frameworks. I don’t know if this was the right choice. The “everything must be known at compile time”-approach nowadays feels a little 2010, to be honest. I don’t understand that the Angular team does not see any usecases when there are many of them across the issues regarding the topic. At least we got webcomponents…

Spoke with @IgorMinar and @alxhub about this. We don’t see any supported use case that benefits from JIT + build optimizer. Build optimizer strips out decorators required by JIT mode, so there’s no easy way to make this happen. Build optimizer is intended for production use and JIT has significant bundle size and performance impact due by its very nature, so you’d never have acceptable bundle sizes with this combination anyways (and if you want build optimizer, you clearly care about bundle size). Compiling components at runtime is also an XSS vulnerability in itself, so doing so is not a recommended or supported use case. As a result, we don’t think Angular should support such a configuration.

I solved my problems by stepping out of the Angular bubble and implementing Web Components. I did not use Angular Elements since you have to deal with the same issues compared to the AOT compile. I implemented plain simple components in TypeScript and they run in every browser needed. The critical thing was to open up a new Shadow DOM in order to isolate the component styles from the app and the other way around. This way I can receive HTML and CSS styles from the web API and process them in my individual web components without the risk of the incoming styles breaking my app. No Angular compiler, no bloated js, just using browser capabilities.

@loedeman @alan-agius4 I am in the same boat, receiving templates from an API (#18139).

“BuildOptimizer, indeed cannot be used with JIT code.”

At the moment there is no story to official support a mixture of both AOT and JIT in the same application. The current stories are exclusive, you either use JIT or AOT.

These sentences worry me a lot, actually. I had no idea that those points are actually a thing. I have never come accross any statement or documentation stating the above. Correct me if I am wrong. I know a lot of developers doing the same thing as we do, relying on JIT and AOT including using buildOptimizer: true. Even on big developer conferences you see these kind of things farely often. If I had known better, I probably would have never chosen Angular as a framework for our project. Again, I am a little bit shocked right now.

The thing is: It actually makes a lot of sense in my opinion. Compiling the “static” part of the app AOT and feeding in a couple dynamically compiled components is an extremely flexible approach.

I hope there will be an official solution for these kind of implementations. Otherwise I am pretty much “trapped” in NG 8.2.14 😢

@alan-agius4 Okay, I get your point. However, we have a small scenario in which the Angular template for grid columns can only be served from the database. Is there another way to fulfill such a scenario, other than having a ton of extra Javascript (we are talking tens of megabytes difference)?

Since #17947 has been closed as a duplicate and this one seems not to contain a solution yet:

I just created a reproduction scenario containing a dynamic component. Here it is: Angular JIT Production. The readme describes how to quickly reproduce the error mentioned above. Hope this gets resolved, because this currently prevents us from deploying production builds to our customers.

@JoostK already dived deep into the issue and commented to have found the issue: Build optimizer incorrectly considers @angular/compiler as having no side-effects, despite its package.json specifying “sideEffects”: true. The culprit is here:

angular-cli/packages/angular_devkit/build_optimizer/src/build-optimizer/build-optimizer.ts
Line 28 in ba0f7ac

 /[\\/]node_modules[\\/]@angular[\\/]compiler[\\/]/, 

Removing that line fixes the issue.