Skip to content

Firebase packages doesn't work inside ESM environment #4241

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
the-spyke opened this issue Dec 26, 2020 · 9 comments
Closed

Firebase packages doesn't work inside ESM environment #4241

the-spyke opened this issue Dec 26, 2020 · 9 comments

Comments

@the-spyke
Copy link

the-spyke commented Dec 26, 2020

[REQUIRED] Describe your environment

  • Operating System version: Ubuntu 20.10
  • Browser version: Chrome 87
  • Firebase SDK version: 8.2.1
  • Firebase Product: app, auth
  • Webpack 5, Node 15, React, regular stuff.

[REQUIRED] Describe the problem

I've decided to give Firebase a try and started with a CodeLab.

1. You need to add the Firebase libraries to the app.

Let's do this. Docs say npm install --save firebase. Easy, but doesn't sound right. Single package for all Firebase features? Let's see what's inside... exports from @firebase packages and the text "This package is not intended for direct usage". Why would you do this?

Continuing the Lab:

// Firebase App (the core Firebase SDK) is always required
// and must be listed first
import * as firebase from "firebase/app";

Wait, was it really a namespace export? Maybe I forgot.

// node_modules/firebase/app/dist/index.esm.js
export { default } from '@firebase/app';

Nope. It's a default export, so should be corrected in the Lab as:

import firebase from "firebase/app";

Also, why are those node_modules/firebase/firebase-app.js laying around? What is the point of installing a @firebase/app dependency and bundling a copy under different name? Docs don't even mention these files. And the directory node_modules/firebase/app has its own package.json with invalid name.

What if I'll try to build the app?

ERROR in ./src/firebase.js 1:0-36
Module not found: Error: Can't resolve 'firebase/app' in '/home/user/myapp/src'
Did you mean 'index.esm.js'?
BREAKING CHANGE: The request 'firebase/app' failed to resolve only because it was resolved as fully specified
(probably because the origin is a '*.mjs' file or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

Of course, ESM packages (and mine has "type": "module") and modules should use full paths (URLs) by the spec, because there's no guessing anymore as Node did before. If you want to leave old shortcuts like /app, then you may specify exports map in the package.json.

So, in 2021 I wish to see better and idiomatic JavaScript packages, especially for web apps:

  • with working ESM by the spec
  • without multiplied assets
  • without unnecessary re-exports
  • hopefully without side effects like import "firebase/auth";

It will be not only easier to consume your products, but also to mind about my app dependencies, and will help to improve tree shaking and reduce bundle size. It will also solve my Webpack issue. For now I could overcome it with resolve.alias overrides. Thanks!

@google-oss-bot
Copy link
Contributor

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

@akauppi
Copy link

akauppi commented Jan 8, 2021

@the-spyke If you are still interested in giving Firebase a go, in ES surroundings, I can help you 1-on-1. I've got a repo working that does that but it's not 100% ready for prime yet. As a general solution I think the more modular "exp" track that Firebase is working on, and which will be released for alpha/beta in some months, might be the right answer.

So: At the moment Firebase + ESM can work together, but there are quirks. In later 2021, they hopefully work better together.

@the-spyke
Copy link
Author

the-spyke commented Jan 8, 2021

@akauppi Thanks, I'm fine. As I said I have already implemented a quirk in my Webpack config:

resolve: {
  alias: {
    "firebase/app$": "firebase/app/dist/index.esm.js",
    "firebase/auth$": "firebase/auth/dist/index.esm.js",
  }
}

Just wanted to make a ticket to track the ESM support and leave some feedback. I hope the "exp" track will also allow to do

import { GoogleAuthProvider } from "firebase/auth";

const uiConfig = {
  signInOptions: [
    GoogleAuthProvider.PROVIDER_ID,
  ],
};

Instead of

import firebase from "firebase/app";

const uiConfig = {
  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
  ],
};

My favorite part is that I also have

import firebase from "firebase/app";

const firebaseApp = firebase.initializeApp(config);

And firebase.auth() returns same instances as firebaseApp.auth(), but firebase.auth has additional props like those providers which firebaseApp.auth hasn't. Everything is so complicated. Thanks.

@Feiyang1
Copy link
Member

Thanks for the report. We changed to use default export since 8.0.0. We update all snippets in guides, but missed the codelab, so good catch. @samtstern How do we update the codelab?

As @akauppi pointed out, we are working on a modular version of the SDK which will soon enter public alpha. It will solve some of the shortcomings you noticed in our SDK today, e.g. side effect imports and the lack of ability to tree shake unused code. You can follow the progress here.

As to working ESM by the spec, I assume you mean native ESM imports in browser. That can't be solved by the library itself because we don't know how your project will be deployed. There is no way to know what absolute URL to use in the library code. You need to rely on tools like snowpack to compile your project and convert package names to absolute URLs. As a library, we will provide an esm build that can be used by these tools.

@Feiyang1
Copy link
Member

Positive. the exp version will allow this. No more namespace nesting.

Just wanted to make a ticket to track the ESM support and leave some feedback. I hope the "exp" track will also allow to do

import { GoogleAuthProvider } from "firebase/auth";

const uiConfig = {
  signInOptions: [
    GoogleAuthProvider.PROVIDER_ID,
  ],
};

Instead of

import firebase from "firebase/app";

const uiConfig = {
  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
  ],
};

The "exp" track will do away all these complexities. Everything will be named exports.

And firebase.auth() returns same instances as firebaseApp.auth(), but firebase.auth has additional props like those providers which firebaseApp.auth hasn't. Everything is so complicated. Thanks.

@the-spyke
Copy link
Author

@Feiyang1 ESM spec for packages means Node's spec. There specific requirements like having "type": "module", using paths with extensions (like ./lib/index.js instead of ./lib/index) or defining exports map in the package.json.

You could try for yourself and use your packages inside an ESM project (type=module) and set module type to javascript/esm in Webpack or enable ESM in Jest. There're many errors.

@Feiyang1
Copy link
Member

Ah I see what you mean now. Sorry for the misunderstanding. I opened #3069 a while ago, but never got around to it. I should fix it soon since v14 has entered LTS and more people are upgrading to it now.

Do you mind if I close this issue since there are open issues that seem to cover the issues you report here? For the SDK rewrite, can you follow #332, and #3069 for better Node's spec compliance ?

@the-spyke
Copy link
Author

@Feiyang1 Sure

@samtstern
Copy link
Contributor

I'm a bit late to the party but I will get the codelab updated now!

@firebase firebase locked and limited conversation to collaborators Feb 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants