Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

add firebase requirement to CommonJS support #625

Closed
wants to merge 1 commit into from

Conversation

drpicox
Copy link

@drpicox drpicox commented Jun 6, 2015

The idea of this pull request is to simplify the development of apps using firebase and angularfire with browserify. Instead of requesting the programmer to add two explicit dependences (angularfire and firebase, one direct and another required by the former), it translates to angularfire the responsability to load firebase dependence (the latter).

Before this commit, using browserify, in order to use angularfire the programmer must do:

$ npm install --save firebase angularfire
require('firebase');
require('angularfire');

With this commit applied, the programmer can drop the direct dependence with firebase:

$ npm install --save angularfire
require('angularfire');

No more dependences or worries are added.

Before this commit, using browserify, in order to use angularfire the programmer must do:

```bash
$ npm install --save firebase angularfire
```
```javascript
require('firebase');
require('angularfire');
```

With this commit applied, the programmer can drop the direct dependence with firebase:

```bash
$ npm install --save angularfire
```

```javascript
require('angularfire');
```

No more dependences or worries are added.
@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project, in which case you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@drpicox
Copy link
Author

drpicox commented Jun 6, 2015

I signed it!

On 6 June 2015 at 10:53, googlebot [email protected] wrote:

Thanks for your pull request. It looks like this may be your first
contribution to a Google open source project, in which case you'll need to
sign a Contributor License Agreement (CLA).

[image: 📝] Please visit https://cla.developers.google.com/
https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll

verify. Thanks.


Reply to this email directly or view it on GitHub
#625 (comment).

Dr. David Rodenas
[email protected]

@jwngr
Copy link

jwngr commented Jun 8, 2015

This seems reasonable to me although I will defer to our resident browserify expert @bendrucker on if this is kosher. Ben, what are your thoughts?

@drpicox
Copy link
Author

drpicox commented Jun 8, 2015

As far as I know there are two potentially risks (but I'm not sure):

If it is accepted: If anyone is installing and requiring manually firebase, it is possible that it becomes duplicated in the JS.

If it is not accepted: firebase and angularfire are not forced to match, so, it can be broken with an update of any of both libraries.

I'm not sure, but another possibility could be use a peer-dependencies, but, because I do not have tested it I didn't put it here.

@bendrucker
Copy link
Contributor

Definitely against this. The original code snippet makes the wrong assumption and I don't see what's gained by this.

it translates to angularfire the responsability to load firebase dependence

We don't want this because angularfire doesn't depend on Firebase anywhere. It depends on the result of a Firebase call.

The actual code would look like:

var Firebase = require('firebase')
var angularfire = require('angularfire')

Requiring things without assignments is code smell, unless we're talking about polyfills. Because AngularFire no longer calls Firebase internally tossing a require in there is incorrect.

If anyone is installing and requiring manually firebase, it is possible that it becomes duplicated in the JS

Browserify is smart about that and won't duplicate

use a peer-dependencies

A peer dep would be a correct change (but solves an entirely unrelated problem). But since Firebase doesn't tend to ship breaking changes, that's more trouble than it's worth.

In theory, v1 and v2 of Firebase could be totally API incompatible according to semver. That means AF would want to specify that it expects to be installed alongside Firebase v2 and should complain if it sees v1. Since those sorts of breaking changes don't happen, I'd skip the peerDep for now and come back to if you ever want to drop support for an old version of the client.

@drpicox
Copy link
Author

drpicox commented Jun 8, 2015

100% agree with @bendrucker

The reason of making angularfire to require Firebase is because the user is supposed to work with angularfire and do nothing with Firebase direcly. So, it does not seems very good idea to ask to the user to load a library with sideefects (global variables), a library which is a dependence of the one that the user actually wants to use. Is like to ask people to require lodash to use grunt. (Here I discuss the concept, not the proposed implementation).

The problem is that in the current implementation angularfire is currently:

  • expecting a window variable called Firebase (it uses the same pattern that angular does to be compatible with Common/JS):
  • expecting a window variable called angular (which has its own global modules),
  • publishing a global module in angular called 'firebase'

Excerpt from: angularfire/dist/angularfire.js

(function(exports) {
  "use strict";

// Define the `firebase` module under which all AngularFire
// services will live.
  angular.module("firebase", [])
    //todo use $window
    .value("Firebase", exports.Firebase);

})(window);

( https://github.com/firebase/angularfire/blob/v1.1.1/dist/angularfire.js#L12-21 )

Main module of angularfile package is angularfire/index.js which exports a string:

require('./dist/angularfire');
module.exports = 'firebase';

( https://github.com/firebase/angularfire/blob/v1.1.1/index.js )

This pattern is the same (or almost the same) adopted by angular in the oficial packages:

It does not mean that it is ok, but it is probably the inspiration of current sources. Probably the main reason of this schema is to make the same source compatible with bower and npm without adding explicit code. The problem is that means that we have global variables and global state. This situation is expected to be solved in Angular 2.

Meanwhile this pullrequest may be a little sweet to ease developments (make them more consistent with browserify angular developments).

@bendrucker
Copy link
Contributor

It does not mean that it is ok, but it is probably the inspiration of current sources

I landed the PRs to do this on Angular so if you disagree you know who to talk to :)

The point of exporting strings is so you can do this:

require('angular').module('myFbApp', [require('angularfire')]

If I'm understanding right, the concern is that you can't inject Firebase because it's not necessarily on the window yet when the value is created w/in Angular. That's totally valid, but the problem there is that Firebase shouldn't be injectable at all. It's a bad idea to expect users to supply already constructed objects but then also make assumptions about the constructor being globally available.

It's a breaking change to rip that out. The answer for Browserify users is to not bother with the dependency injector because you have a better way to get a hold of Firebase (require('firebase'))

@drpicox
Copy link
Author

drpicox commented Jun 8, 2015

I do not disagree, in fact, in my developments I use this formula. And of course, my newest devs packages are 100% npm packages, without assuming global variables.

The concern is that I do not need any instance of Firebase, not in the window not in the app. It is angularfire who requires it, but, instead of loading the dependence by itself, it asks you to do it.

@jamestalmage
Copy link
Contributor

The concern is that I do not need any instance of Firebase, not in the window not in the app. It is angularfire who requires it, but, instead of loading the dependence by itself, it asks you to do it.

I do not see how that is possible. All major functions of the angularFire library require you to pass in a Firebase reference. Your are not going to be able to provide that without requiring the constructor some where in your code. For example, how does it help with this (modified from the quickstart example):

var Firebase = require('firebase');
app.controller("SampleCtrl", function($scope, $firebaseArray) {
  // you are going to need this constructor call somewhere!
  var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/messages");
  $scope.messages = $firebaseArray(ref);
});

@drpicox - If you still feel strongly about this, can you create a minimal sample project that demonstrates how this helps. As of right now, I don't see it.

Also, FYI, the decision to export a string goes back to #567 from this repo, and #10732 from angular.js. The decision to do so was given a lot of thought and discussion, so you are fighting a steep uphill battle if you want to change that practice.

@bendrucker
Copy link
Contributor

Right, so the correct answer is to not register Firebase with the injector at all. Technically AngularFire depends on Firebase in order to make it injectable. The ideal move here is to cut the window.Firebase reference, but that's not a breaking change worth making.

This is an equally breaking change with equally little benefit.

@jwngr my final rec is to close this and cut the value call in a future major version

@jamestalmage
Copy link
Contributor

Maybe we should deprecate injection via angular-di immediately, but not break anything until later.
That should still satisfy semver, while still pushing users towards correct usage patterns.

(function(exports) {
  "use strict";
  var log = true;
   // Define the `firebase` module under which all AngularFire
   // services will live.
  angular.module("firebase", [])
    //todo use $window
    .value("Firebase",  function(url) {
        if (log) {
           log = false; // only log once.
           console.log("Injecting the Firebase constructor via angular-di is deprecated. Either access it via the global variable, or require it via CommonJS");
        }
        return new exports.Firebase(url); // does it need to handle more than one argument?
   });
})(window);

@drpicox
Copy link
Author

drpicox commented Jun 9, 2015

Ops, that's right.
I didn't notice it but I use a Firebase instance from angularfire, which comes from the angular injector and I use it only to configure the url. My apologies.

Anyway, before deprecating it, you should consider that angular DI is far more advanced than npm require (accepts redefinitions, decorators, ...).

@jamestalmage
Copy link
Contributor

AngularFire never actually calls new Firebase anywhere. The Firebase refs are always passed in via constructor injection for both $firebaseObject and $firebaseArray. The angular value reference is really just a convenience item for those using angular-di.

If you are talking redefining / decorating dependencies during testing, then:

  • proxyquire is your solution for node.
  • proxyquireify for browser testing via browserify (use this for most of your angular stuff).
  • proxyquire-universal for code tests you want to run in both places.

I think proxyquire provides a much cleaner API.

As for decorating instances for production, angular-di may be your best choice.

@drpicox
Copy link
Author

drpicox commented Jun 9, 2015

So, if I understood well, this:

should be a peer-dependence at most, right?

@bendrucker
Copy link
Contributor

Proxyquire seems pretty neat ;)

I wrote proxyquire-universal and maintain proxyquire(ify). They're pretty great and Thorsten deserves all the credit for the API and the implementation.

should be a peer-dependence at most, right

You are correct (the code isn't).

@jwngr
Copy link

jwngr commented Jun 12, 2015

Thanks for all the discussion here folks. I'm closing this issue per @bendrucker's suggestion.

I doubt you will see any changes on this front for AngularFire for Angular 1.x. We are in maintenance mode with this library and will be focusing efforts on AngularFire for Angular 2.x going forward (although that doesn't actually exist yet). We will re-address this topic when that new library comes out.

FWIW, having Firebase as a peer dep makes sense and is probably what we should have done. But I agree that these other changes bring little to no benefit.

@jwngr jwngr closed this Jun 12, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants