Angular-cli: Dangerous, broken rxjs imports do not surface as errors, break app at runtime

Created on 8 Jan 2017  路  9Comments  路  Source: angular/angular-cli

OS?

Windows 7, 8 or 10. Linux (which distribution). Mac OSX (Yosemite? El Capitan?)
OS X El Capitan

Versions.

```angular-cli: 1.0.0-beta.24
node: 6.9.1
os: darwin x64
@angular/common: 2.4.2
@angular/compiler: 2.4.2
@angular/core: 2.4.2
@angular/forms: 2.4.2
@angular/http: 2.4.2
@angular/platform-browser: 2.4.2
@angular/platform-browser-dynamic: 2.4.2
@angular/router: 3.4.2
@angular/compiler-cli: 2.4.2

### Repro steps.
Please find a repository with the repro at: https://github.com/JohannesRudolph/angular-cli-rxjs-repro at:

The repro contains two components, `ChildComponent`and `OtherComponent`. Each makes use of two rxjs operators/Obervable operations: `Observable.of()` and `.mapTo()`.

However, `ChildComponent` imports only one half of the added operators

// This file imports Obserable.of() and makes use of .mapTo(), imported in other.component.ts
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';

while `OtherComponent` imports the other half: 

// This file imports .mapTo() and makes use of Observable.of(), imported in child.component.ts
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/mapTo';

The fun begins when these two Components are contained in separate, lazy-loaded modules as implemented in the linked sample repository. To produce a runtime error, click the "Child" or "Other" link after running `ng serve` on the repro.

### The log given by the failure.
`ng build --aot` and `ng build` do not log any errors.

When navigating to either lazy-loaded module, e.g. the following runtime error occurs:

EXCEPTION: Uncaught (in promise): Error: Error in :0:0 caused by: __WEBPACK_IMPORTED_MODULE_1_rxjs_Observable__.Observable.of is not a function
TypeError: __WEBPACK_IMPORTED_MODULE_1_rxjs_Observable__.Observable.of is not a function
at OtherComponent.ngOnInit (http://localhost:4200/4.chunk.js:29:77)
```

Mention any other details that might be useful.

It's clear to me why that happens:

  • webpack inlines the .of() operator in the ChildModule bundle/chunk
  • webpack inlines the .mapTo() operator in the OtherModule bundle/chunk

When navigating to either of these chunks while the other has not been loaded yet, the error occurs because the webpack runtime (if that's the correct term) has not added the required rxjs imports from the respective other module. The source of the errors are missing imports that are not detected by neither the tsc nor the aot compiler.

I propose angular-cli (or some other component of the build chain) should handle this scenario by ensuring that each .ts file imports all (statically) referenced operators that it needs. I'm not sure why that doesn't happen already. So to fix this issue we need to pinpoint what part of the build chain is at fault and fix it. Any ideas?

Most helpful comment

Here's a tslint rule coming https://github.com/palantir/tslint/pull/2155.

All 9 comments

those imports are a bit trickier because they patch an observables prototype. I do agree it is annoying that these are not caught at compile time. This may be more of a TS issue rather then an angular-cli issue.

Thanks for your stance on this @deebloo

Since angular is an important consumer of rxjs, I think it makes sense if there was some way this could be caught at compile time since the patching of the Observable prototype is predictable. However, I don't know if typescript has a concept for module prototype patching at all that would enable it to detect this in the compiler.

Until then, our best course of action might be to implement this as a linter rule maybe?

I suppose a linter rule might be helpful, but at best we can add it to the default project.

To be honest I don't think this is a CLI issue... I mean, I understand the concern. I have it myself. Had problems with it myself. But it is a fundamental part of the RxJs architecture. It was built to be used in this manner.

Personally I think it is a matter of documentation, either on RxJs or angular.io.

https://palantir.github.io/tslint/rules/import-blacklist/ could be used overall, while allowing rxjs imports only on a specific file (like main.ts).

Here's a tslint rule coming https://github.com/palantir/tslint/pull/2155.

The rule highlighted by @mgechev helps with this issue, but overall the only solution to using libraries of this kind is import discipline.

I encountered this problem and worked around in our app by putting the following in our vendor imports:

// Third-party
import 'rxjs';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/forkJoin';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/withLatestFrom';
// ...and any other operators you use throughout the app

and removing all rxjs/add/... from the Component imports etc

Seems to work fine with lazy loading and AOT, but I'd be interested if anyone thinks this is a good / bad way to get around it.

We found it way too error prone to try and import the individual operators we needed in the app's files. If we had compile time checking of that though it would probably be fine...

https://ufile.io/lngs7

ERROR TypeError: rxjs__WEBPACK_IMPORTED_MODULE_1__.Observable.of is not a function
at AppComponent.push../src/app/app.component.ts.AppComponent.ngOnInit

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings