Skip to content

Angular Fire Firestore not working with Angular Universal v9 (Ivy) due to issue with Firestore UMD generated by ngcc #2280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jeffwhelpley opened this issue Jan 6, 2020 · 10 comments

Comments

@jeffwhelpley
Copy link

jeffwhelpley commented Jan 6, 2020

Version info

Angular: 9.0.0-rc.7

Firebase: 7.6.1

AngularFire: 5.3.0-rc.4

Other (e.g. Ionic/Cordova, Node, browser, operating system):
node v12.3.1

How to reproduce these conditions

Try to compile and run an Angular Universal app that has a dependency on @angular/fire/firestore. It won't even compile unless you run the ngcc.

If you run ngcc, though, then you will get an error when you run your server like this:

/Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:92536
AngularFirestore.ɵprov = ɵngcc0.ɵɵdefineInjectable({ token: AngularFirestore, factory: function (t) { return AngularFirestore.ɵfac(t); } });
                                ^

TypeError: ɵngcc0.ɵɵdefineInjectable is not a function
    at /Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:92536:33
    at /Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:92567:6
    at /Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:92235:13
    at Object.../../node_modules/@angular/fire/bundles/firestore.umd.js (/Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:92237:2)
    at __webpack_require__ (/Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:20:30)

When I look at the UMD that is generated for Firestore by ngcc, I see this at the beginning:

(function (global, factory) {
     true ? factory(exports, __webpack_require__(/*! rxjs */ "../../node_modules/rxjs/index.js"), __webpack_require__(/*! rxjs/operators */ "../../node_modules/rxjs/operators/index.js"), __webpack_require__(/*! @angular/fire */ "../../node_modules/@angular/fire/bundles/core.umd.js"), __webpack_require__(/*! @angular/core */ "../../node_modules/@angular/core/bundles/core.umd.js"), __webpack_require__(/*! @angular/common */ "../../node_modules/@angular/common/bundles/common.umd.js"), __webpack_require__(/*! firebase/app */ "../../node_modules/firebase/app/dist/index.cjs.js"), __webpack_require__(/*! firebase/firestore */ "../../node_modules/firebase/firestore/dist/index.cjs.js"),__webpack_require__(/*! @angular/core */ "../../node_modules/@angular/core/bundles/core.umd.js")) :
    undefined;
}(this, (function (exports,rxjs,operators,fire,core,common,firebase,ɵngcc0) { 'use strict';

I was debugging this code for awhile to try and figure out what is going on until I realized that the call to the factory() function is sending in more input params (9) than there is actually in the function definition (8). he function has exports,rxjs,operators,fire,core,common,firebase,ɵngcc0 which is 8 params, but here is what the factory is sending in:

1. exports
2. __webpack_require__(/*! rxjs */ "../../node_modules/rxjs/index.js")
3. __webpack_require__( "../../node_modules/rxjs/operators/index.js")
4. __webpack_require__( "../../node_modules/@angular/fire/bundles/core.umd.js")
5. __webpack_require__( "../../node_modules/@angular/core/bundles/core.umd.js")
6. __webpack_require__("../../node_modules/@angular/common/bundles/common.umd.js")
7. __webpack_require__("../../node_modules/firebase/app/dist/index.cjs.js")
8. __webpack_require__("../../node_modules/firebase/firestore/dist/index.cjs.js")
9. __webpack_require__(/*! @angular/core */ "../../node_modules/@angular/core/bundles/core.umd.js")

When you look at the original UMD for Firestore, this starts to make sense because you can see there is an extra param there as well:

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('rxjs/operators'), require('@angular/fire'), require('@angular/core'), require('@angular/common'), require('firebase/app'), require('firebase/firestore')) :
    typeof define === 'function' && define.amd ? define(['exports', 'rxjs', 'rxjs/operators', '@angular/fire', '@angular/core', '@angular/common', 'firebase/app', 'firebase/firestore'], factory) :
    (factory((global.angularfire2 = global.angularfire2 || {}, global.angularfire2.firestore = {}),global.rxjs,global.rxjs.operators,global.angularfire2,global.ng.core,global.ng.common,global.firebase));
}(this, (function (exports,rxjs,operators,fire,core,common,firebase) { 'use strict';

You can see here that require('firebase/firestore') is at the end of the factory() call, but there is no param for that. So, in the original UMD there is no harm because that extra param isn't used. But, ngcc must be adding an extra param without checking for this.

So, I believe at least part of the fix is figuring out why the normal Firestore UMD has that references to /firebase/firestore even though it is not being used.

Steps to set up and reproduce

  1. Set up basic Angular Universal app with Angular v9 similar to this setup
  2. In your package.json scripts section make sure you have "postinstall": "ngcc"
  3. In your angular.json server options section make sure you have "bundleDependencies": true and add externalDependencies there for any native dependencies
  4. Build your browser package
  5. Build your server package
  6. Run the server package

Sample data and security rules

N/A

Debug output

** Errors in the JavaScript console **

N/A

** Output from firebase.database().enableLogging(true); **

N/A

** Screenshots **

N/A

Expected behavior

To not error out

Actual behavior

Get errors listed above.

@jeffwhelpley jeffwhelpley changed the title Firestore UMD package not generated correctly by ngcc Angular Fire Firestore not working with Angular Universal v9 (Ivy) due to issue with Firestore UMD generated by ngcc Jan 6, 2020
@jeffwhelpley
Copy link
Author

jeffwhelpley commented Jan 6, 2020

As a secondary issue which is related, once I hack the Firestore UMD to temporarily fix the issue described above ^^^ (by removing the require('firebase/firestore') param in the UMD), I run into another issue depending on whether or not I add @angular/fire/firestore to my list of externalDependencies in angular.json.

If @angular/fire/firestore is in externalDependencies then I get this error when trying to go to a server route:

Error: Uncaught (in promise): Error: inject() must be called from an injection context
Error: inject() must be called from an injection context
    at injectInjectorOnly (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/@angular/core/bundles/core.umd.js:972:19)
    at Object.ɵɵinject (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/@angular/core/bundles/core.umd.js:983:61)
    at Function.AngularFirestore_Factory [as ɵfac] (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/@angular/fire/bundles/firestore.umd.js:307:106)
    at Object.factory (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/@angular/fire/bundles/firestore.umd.js:308:127)
    at R3Injector.../../node_modules/@angular/core/bundles/core.umd.js.R3Injector.hydrate (/Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:72465:39)

Which I would imagine is because the main server package is using the @angular/core that is part of the main server package while Firestore in this case ^^^ is using @angular/core under node_modules. Thus the injection context is going to be different.

If @angular/fire/firestore is NOT in externalDependencies (i.e. it is packaged along with the main server build) then I get this error:

[2020-01-06T18:42:32.270Z]  @firebase/firestore: Firestore (7.6.0): INTERNAL UNHANDLED ERROR:  Error: ENOENT: no such file or directory, open '/Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/src/protos/google/firestore/v1/firestore.proto'
    at Object.openSync (fs.js:431:3)
    at Object.readFileSync (fs.js:333:35)
    at fetch (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/protobufjs/src/root.js:160:34)
    at Root.load (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/protobufjs/src/root.js:194:13)
    at Root.loadSync (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/protobufjs/src/root.js:235:17)
    at Object.loadSync (/Users/jeffwhelpley/repos/criticly/frontend/node_modules/@grpc/proto-loader/build/src/index.js:221:27)
    at loadProtos (/Users/jeffwhelpley/repos/criticly/frontend/dist/apps/criticly/server/main.js:135816:41)

The reason for this is because of this line of code where one of the downstream dependencies is referencing __dirname:

https://github.com/firebase/firebase-js-sdk/blob/bd5ce34db9c11ab16296b29c5d410b78876a0115/packages/firestore/src/platform_node/load_protos.ts#L39

Since the code has been packaged up with the main server build, that path to firestore.proto is no longer valid.

So, I think one of two fixes clearly have to be made:

A) If firestore an external dependency, then have to figure out some way to maintain the same angular core context (not sure how to do that, though)

B) If firestore is packaged with the server build, need to update the firebase/firestore code so that it can still find that firestore.proto file.

@jeffwhelpley
Copy link
Author

jeffwhelpley commented Jan 6, 2020

OK, so after a lot of hacking compiled module code, I have Angular Universal + Firestore working for Angular v9 (Ivy). There definitely are some fixes needed here. The hacks I had to make are as follows:

  1. Of the various options above, I did set it up to have the Angular server build include @angular/fire/firestore in the package (i.e. in other words, I did NOT add it to the externalDependencies in angular.json). I also had ngcc run as a postinstall step.

  2. In my compiled server module code I manually changed the Firestore UMD bundle factory function so that it doesn't have that extra parameter anymore (but I did have to still require firebase/firestore so that it adds itself to the global firestore object):

(function (global, factory) {

   // I added this so firestore adds itself to the global firebase object
    __webpack_require__(/*! firebase/firestore */ "../../node_modules/firebase/firestore/dist/index.cjs.js");

    // I removed the extra param to the factory here so that there are 8 inputs instead of 9
     true ? factory(exports, __webpack_require__(/*! rxjs */ "../../node_modules/rxjs/index.js"), __webpack_require__(/*! rxjs/operators */ "../../node_modules/rxjs/operators/index.js"), __webpack_require__(/*! @angular/fire */ "../../node_modules/@angular/fire/bundles/core.umd.js"), __webpack_require__(/*! @angular/core */ "../../node_modules/@angular/core/bundles/core.umd.js"), __webpack_require__(/*! @angular/common */ "../../node_modules/@angular/common/bundles/common.umd.js"), __webpack_require__(/*! firebase/app */ "../../node_modules/firebase/app/dist/index.cjs.js"), __webpack_require__(/*! @angular/core */ "../../node_modules/@angular/core/bundles/core.umd.js")) :
    undefined;
}(this, (function (exports,rxjs,operators,fire,core,common,firebase,ɵngcc0) { 'use strict';

I am not 100% but I believe ultimately this issue comes from this line in the source code:

https://github.com/angular/angularfire/blob/master/src/firestore/firestore.ts#L17

You can see the comment about it being there just for Angular 6 support. That firestore import reference isn't used anywhere. So I believe when the UMD module is built during the publishing process, it must result in that extra param in the UMD factory function. It is a hack, but I am pretty sure a super quick fix for this would be to use that firestore import value so that rollup or whatever is doing the compiling doesn't remove it.

  1. In my compiled server module code I manually changed the root so it didn't get messed up with __dirname:
function loadProtos() {^M
    //var root = path.resolve(__dirname, "src/protos" );^M
    var root = "/Users/jeffwhelpley/repos/criticly/frontend/node_modules/@firebase/firestore/dist/src/protos";
    var firestoreProtoFile = path.join(root, 'google/firestore/v1/firestore.proto');^M
    var packageDefinition = protoLoader.loadSync(firestoreProtoFile, tslib.__assign(tslib.__assign({}, protoLoaderOptions), { includeDirs: [root] }));^M
    return grpc.loadPackageDefinition(packageDefinition);^M
}

Now, interestingly, if you look at the source for this one:

https://github.com/firebase/firebase-js-sdk/blob/bd5ce34db9c11ab16296b29c5d410b78876a0115/packages/firestore/src/platform_node/load_protos.ts#L39

You will see that there is a possibility to set FIRESTORE_PROTO_ROOT. However, that doesn't appear in the compiled code. So, the solution for this part of the issue should be to make it so the user can configure the FIRESTORE_PROTO_ROOT at run time. You cannot right now because it is not in the compiled code. Ideally this would be a configurable value in the AngularFire module (or AngularFire would just automatically set this correctly since it shouldn't change for Angular users).

@alan-agius4
Copy link
Contributor

alan-agius4 commented Jan 6, 2020

@jeffwhelpley, FYI with regards to the FIRESTORE_PROTO_ROOT the environment variable itself is not available in the distributed version.

See: https://unpkg.com/browse/@firebase/[email protected]/dist/index.node.cjs.js - Line 23213

Regarding the UMD processing there is seems to be a bug as correctly mentioned above, however I didn't need to run NGCC as a postinstall hook as @angular/fire/store was processed even without it.

Maybe you can share a repo where your are encountering this issue?

PS: This issues is more of an Angular FW thing, rather than Angular Fire.

@petebacondarwin
Copy link

Here is a PR for the ngcc side of things: angular/angular#34660

@jeffwhelpley
Copy link
Author

Here is a public repo with the issue: https://github.com/jeffwhelpley/ngufire. Follow the instructions in the README to recreate the issue.

@jamesdaniels
Copy link
Member

Thanks for spelunking through this! Once we drop ng6 we'll be able to use Typescript versions that support dynamic types, typeof import('blah'). The lack of this feature is why I have "unused" imports.

I'm going to start developing v6 off of master today; expect me to drop those soon. Keep an eye out for @canary builds.

@jamesdaniels
Copy link
Member

Should be addressed in 6.0.0-rc.0 reopen if you are continuing to have trouble.

@Kushalbaldev
Copy link

Getting this error in version 10 of angular

This likely means that the library (@angular/fire/firestore) which declares AngularFirestore has not been processed correctly by ngcc, or is not compatible with Angular Ivy. Check if a newer version of the library is available, and update if so. Also consider checking with the library's authors to see if the library is expected to be compatible with Ivy.

@OmarHegazi94
Copy link

any update on this issue ?

@jamesdaniels
Copy link
Member

@OmarHegazi94 there's no issue here any longer. We're fully compatible with IVY and the latest versions of Angular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants