Skip to content

Commit

Permalink
fix(auth): allow treeshaking with explicit initialization
Browse files Browse the repository at this point in the history
Fix #1459
  • Loading branch information
posva committed Dec 1, 2023
1 parent a013306 commit 7d94183
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 19 deletions.
17 changes: 16 additions & 1 deletion playground/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { createFirebaseApp } from './firebase'
import { createWebHistory, createRouter } from 'vue-router/auto'
import { createStore } from 'vuex'
import { ReCaptchaV3Provider } from 'firebase/app-check'
import { VueFireAuthWithDependencies } from '../../src/auth'
import {
browserLocalPersistence,
browserPopupRedirectResolver,
indexedDBLocalPersistence,
} from 'firebase/auth'

const router = createRouter({
history: createWebHistory(),
Expand Down Expand Up @@ -43,7 +49,16 @@ app
.use(VueFire, {
firebaseApp: createFirebaseApp(),
modules: [
VueFireAuth(),
VueFireAuthWithDependencies({
dependencies: {
popupRedirectResolver: browserPopupRedirectResolver,
persistence: [
indexedDBLocalPersistence,
browserLocalPersistence,
// browserSessionPersistence,
],
},
}),
VueFireAppCheck({
debug: process.env.NODE_ENV !== 'production',
isTokenAutoRefreshEnabled: true,
Expand Down
106 changes: 92 additions & 14 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import type { FirebaseApp } from 'firebase/app'
import { getAuth, type User } from 'firebase/auth'
import { App, ref } from 'vue-demi'
import {
type Dependencies as AuthDependencies,
initializeAuth,
type User,
browserPopupRedirectResolver,
browserLocalPersistence,
browserSessionPersistence,
indexedDBLocalPersistence,
Auth,
} from 'firebase/auth'
import { type App, ref, inject } from 'vue-demi'
import { useFirebaseApp } from '../app'
import { getGlobalScope } from '../globals'
import { isClient, _Nullable } from '../shared'
import { authUserMap, setupOnAuthStateChanged } from './user'
import { type VueFireModule } from '..'

export {
useCurrentUser,
Expand All @@ -15,28 +25,80 @@ export {
} from './user'

/**
* VueFire Auth Module to be added to the `VueFire` Vue plugin options.
* Options for VueFire Auth module.
*/
export interface VueFireAuthOptions {
/**
* Initial value of the user. Used during SSR.
*/
initialUser?: _Nullable<User>

/**
* Options to pass to `initializeAuth()`.
*/
dependencies: AuthDependencies
}

/**
* VueFire Auth Module to be added to the `VueFire` Vue plugin options. This calls the `VueFireAuthWithDependencies()`
* with **all** the dependencies, increasing bundle size. Consider using `VueFireAuthWithDependencies()` instead to
* better control the bundle size.
*
* @see https://firebase.google.com/docs/auth/web/custom-dependencies
*
* @example
*
* ```ts
* import { createApp } from 'vue'
* import { VueFire, VueFireAuth } from 'vuefire'
*```ts
*import { createApp } from 'vue'
*import { VueFire, VueFireAuth } from 'vuefire'
*
*const app = createApp(App)
*app.use(VueFire, {
* modules: [VueFireAuth()],
*})
*```
*
* @param initialUser - initial value of the user. used for SSR
*/
export function VueFireAuth(initialUser?: _Nullable<User>): VueFireModule {
return VueFireAuthWithDependencies({
initialUser,
dependencies: {
popupRedirectResolver: browserPopupRedirectResolver,
persistence: [
indexedDBLocalPersistence,
browserLocalPersistence,
browserSessionPersistence,
],
},
})
}

/**
* Key to be used to inject the auth instance into components. It allows avoiding to call `getAuth()`, which isn't tree
* shakable.
*/
export const _VueFireAuthKey = Symbol('VueFireAuth')

/**
* VueFire Auth Module to be added to the `VueFire` Vue plugin options. It accepts dependencies to pass to
* `initializeAuth()` to better control the bundle size.
*
* const app = createApp(App)
* app.use(VueFire, {
* modules: [VueFireAuth()],
* })
* ```
* @param options - user and options to pass to `initializeAuth()`.
*/
export function VueFireAuth(initialUser?: _Nullable<User>) {
export function VueFireAuthWithDependencies({
dependencies,
initialUser,
}: VueFireAuthOptions): VueFireModule {
return (firebaseApp: FirebaseApp, app: App) => {
const user = getGlobalScope(firebaseApp, app).run(() =>
ref<_Nullable<User>>(initialUser)
)!
// this should only be on client
authUserMap.set(firebaseApp, user)
setupOnAuthStateChanged(user, firebaseApp)
const auth = initializeAuth(firebaseApp, dependencies)
app.provide(_VueFireAuthKey, auth)
setupOnAuthStateChanged(user, auth)
}
}

Expand All @@ -47,6 +109,22 @@ export function VueFireAuth(initialUser?: _Nullable<User>) {
* @param name - name of the application
* @returns the Auth instance
*/
export function useFirebaseAuth(): Auth | null
/**
* Retrieves the Firebase Auth instance. **Returns `null` on the server**. When using this function on the client in
* TypeScript, you can force the type with `useFirebaseAuth()!`.
*
* @deprecated - the name parameter is removed to enable tree shaking. If you have multiple applications, you **must**
* use "getAuth(firebaseApp)" or "getAuth(useFirebaseApp(name))" instead.`
*
* @param name - name of the application
* @returns the Auth instance
*/
export function useFirebaseAuth(name?: string) {
return isClient ? getAuth(useFirebaseApp(name)) : null
if (__DEV__ && name != null) {
console.warn(
`[VueFire] useFirebaseAuth() no longer accepts a name parameter to enable tree shaking. If you have multiple applications, you must use "getAuth(firebaseApp)" or "getAuth(useFirebaseApp(name))" instead.`
)
}
return isClient ? inject(_VueFireAuthKey) : null
}
6 changes: 2 additions & 4 deletions src/auth/user.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { FirebaseApp } from 'firebase/app'
import {
getAuth,
onIdTokenChanged,
type User,
updateEmail,
updateProfile,
reauthenticateWithCredential,
type AuthCredential,
type Auth,
} from 'firebase/auth'
import { computed, Ref } from 'vue-demi'
import { useFirebaseApp } from '../app'
Expand Down Expand Up @@ -168,10 +168,8 @@ export function getCurrentUser(name?: string): Promise<_Nullable<User>> {

export function setupOnAuthStateChanged(
user: Ref<_Nullable<User>>,
app?: FirebaseApp
auth: Auth
) {
const auth = getAuth(app)

// onAuthStateChanged doesn't trigger in all scenarios like when the user goes links an existing account and their
// data is updated
// https://github.com/firebase/firebase-js-sdk/issues/4227
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ export { useFirebaseApp } from './app'
export {
useCurrentUser,
useIsCurrentUserLoaded,
type VueFireAuthOptions,
VueFireAuth,
VueFireAuthWithDependencies,
useFirebaseAuth,
getCurrentUser,
updateCurrentUserProfile,
Expand Down

0 comments on commit 7d94183

Please sign in to comment.