From 92f1c6e9ae27996cac2de237d14905678f9547e6 Mon Sep 17 00:00:00 2001 From: Dominic Garms Date: Sat, 27 Mar 2021 11:10:49 +0800 Subject: [PATCH 01/29] add options to useAuthState hook "onUserChanged" Add possibility to pass options for onUserLoaded, a function which is called if a user auth state changed. This might close https://github.com/CSFrequency/react-firebase-hooks/issues/33 --- auth/useAuthState.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index fad14ed..c35e9eb 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -1,5 +1,5 @@ import firebase from 'firebase/app'; -import { useEffect, useMemo } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { LoadingHook, useLoadingValue } from '../util'; export type AuthStateHook = LoadingHook< @@ -7,14 +7,20 @@ export type AuthStateHook = LoadingHook< firebase.auth.Error >; -export default (auth: firebase.auth.Auth): AuthStateHook => { +export default (auth: firebase.auth.Auth, options: {onUserChanged?: Promise}): AuthStateHook => { const { error, loading, setError, setValue, value } = useLoadingValue< firebase.User | null, firebase.auth.Error >(() => auth.currentUser); useEffect(() => { - const listener = auth.onAuthStateChanged(setValue, setError); + const listener = auth.onAuthStateChanged(async (user) => { + if(typeof options?.onUserChanged === 'function') { + // onUserLoaded function to process custom claims on any other trigger function + await options.onUserChanged(user) + } + setValue(user); + }, setError); return () => { listener(); From b6d7a841a8efb6b5fa1094e4c2e1508fbd5e383e Mon Sep 17 00:00:00 2001 From: Dominic Garms Date: Sat, 27 Mar 2021 11:11:51 +0800 Subject: [PATCH 02/29] Update useAuthState.ts --- auth/useAuthState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index c35e9eb..2dfaa70 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -1,5 +1,5 @@ import firebase from 'firebase/app'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo } from 'react'; import { LoadingHook, useLoadingValue } from '../util'; export type AuthStateHook = LoadingHook< From 8a0c2ec27b307eb858f5e4929a5191ba16af9bc5 Mon Sep 17 00:00:00 2001 From: Dominic Garms Date: Sat, 27 Mar 2021 11:13:31 +0800 Subject: [PATCH 03/29] Update useAuthState.ts --- auth/useAuthState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 2dfaa70..17ae40e 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -7,7 +7,7 @@ export type AuthStateHook = LoadingHook< firebase.auth.Error >; -export default (auth: firebase.auth.Auth, options: {onUserChanged?: Promise}): AuthStateHook => { +export default (auth: firebase.auth.Auth, options: {onUserChanged?: (user?: firebase.User) => Promise}): AuthStateHook => { const { error, loading, setError, setValue, value } = useLoadingValue< firebase.User | null, firebase.auth.Error From a898bb5cd3da9cd6affe058b0451655f9e31ab53 Mon Sep 17 00:00:00 2001 From: Dominic Garms Date: Sun, 25 Apr 2021 15:47:28 +0800 Subject: [PATCH 04/29] Update useAuthState.ts --- auth/useAuthState.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 17ae40e..088d3de 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -17,7 +17,11 @@ export default (auth: firebase.auth.Auth, options: {onUserChanged?: (user?: fire const listener = auth.onAuthStateChanged(async (user) => { if(typeof options?.onUserChanged === 'function') { // onUserLoaded function to process custom claims on any other trigger function - await options.onUserChanged(user) + try { + await options.onUserChanged(user) + } catch(e) { + setError(e) + } } setValue(user); }, setError); From 652c525a4395561ece116489f42021f170747da2 Mon Sep 17 00:00:00 2001 From: Isaiah Solomon Date: Fri, 7 May 2021 23:47:35 -0700 Subject: [PATCH 05/29] add useSignInWithGoogle hook --- auth/types.ts | 7 +++++++ auth/useSignInWithGoogle.ts | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 auth/useSignInWithGoogle.ts diff --git a/auth/types.ts b/auth/types.ts index 3f32831..7027f10 100644 --- a/auth/types.ts +++ b/auth/types.ts @@ -14,3 +14,10 @@ export type EmailAndPasswordActionHook = AuthActionHook< firebase.auth.UserCredential, firebase.FirebaseError >; + +export type GoogleActionHook = [ + () => void, + firebase.auth.UserCredential | undefined, + boolean, + firebase.FirebaseError | undefined +]; diff --git a/auth/useSignInWithGoogle.ts b/auth/useSignInWithGoogle.ts new file mode 100644 index 0000000..b25cd05 --- /dev/null +++ b/auth/useSignInWithGoogle.ts @@ -0,0 +1,36 @@ +import { useState, useMemo } from 'react'; +import firebase from 'firebase/app'; +import { GoogleActionHook } from './types'; + +export default ( + auth: firebase.auth.Auth, + extraScopes: Array = [] +): GoogleActionHook => { + const [error, setError] = useState(); + const [ + loggedInUser, + setLoggedInUser, + ] = useState(); + const [loading, setLoading] = useState(false); + + const signInWithGoogle = async () => { + setLoading(true); + try { + const provider = new firebase.auth.GoogleAuthProvider(); + extraScopes.forEach((extraScope) => provider.addScope(extraScope)); + await auth.signInWithPopup(provider).then(setLoggedInUser); + setLoading(false); + } catch (err) { + setError(err); + setLoading(false); + } + }; + + const resArray: GoogleActionHook = [ + signInWithGoogle, + loggedInUser, + loading, + error, + ]; + return useMemo(() => resArray, resArray); +}; From 9977f71736360cf7818a5d7bc64b6ccb0d64cced Mon Sep 17 00:00:00 2001 From: Johan Niwhede Date: Mon, 29 Nov 2021 20:14:57 +0100 Subject: [PATCH 06/29] fix: Incorrect parentheses in README --- firestore/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firestore/README.md b/firestore/README.md index 4dfd357..30f59b1 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -190,7 +190,7 @@ import { useDocument } from 'react-firebase-hooks/firestore'; const FirestoreDocument = () => { const [value, loading, error] = useDocument( - doc(getFirestore(firebaseApp, 'hooks', 'nBShXiRGFAhuiPfBaGpt')), + doc(getFirestore(firebaseApp), 'hooks', 'nBShXiRGFAhuiPfBaGpt'), { snapshotListenOptions: { includeMetadataChanges: true }, } From a3241e11cc4ffdf7f19ce814c4819f5c8070b1ad Mon Sep 17 00:00:00 2001 From: Matsuoka <39664774+elpnt@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:00:04 +0900 Subject: [PATCH 07/29] Fix: Incorrect arguments in README Fixed incorrect arguments for `useSignInWithEmailAndPassword` hook --- auth/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/README.md b/auth/README.md index 81b0d40..dc087e5 100644 --- a/auth/README.md +++ b/auth/README.md @@ -165,7 +165,7 @@ const [ user, loading, error, -] = useSignInWithEmailAndPassword(auth, email, password); +] = useSignInWithEmailAndPassword(auth); ``` Login a user with email and password. Wraps the underlying `auth.signInWithEmailAndPassword` method and provides additional `loading` and `error` information. From d9375eb21a7b2e21b444a22aa62bd4e670d1f534 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 21 Dec 2021 16:17:52 +0000 Subject: [PATCH 08/29] [firestore] Remove manual snapshotToData functionality --- firestore/README.md | 54 ++++++++++++-------------------------- firestore/helpers/index.ts | 28 -------------------- firestore/types.ts | 23 ++++------------ firestore/useCollection.ts | 54 +++++++++----------------------------- firestore/useDocument.ts | 53 +++++++++---------------------------- 5 files changed, 47 insertions(+), 165 deletions(-) diff --git a/firestore/README.md b/firestore/README.md index 30f59b1..be356bd 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -19,16 +19,16 @@ import { useCollection } from 'react-firebase-hooks/firestore'; List of Cloud Firestore hooks: - [React Firebase Hooks - Cloud Firestore](#react-firebase-hooks---cloud-firestore) - - [useCollection](#usecollection) - - [Full example](#full-example) - - [useCollectionOnce](#usecollectiononce) - - [useCollectionData](#usecollectiondata) - - [useCollectionDataOnce](#usecollectiondataonce) - - [useDocument](#usedocument) - - [Full example](#full-example-1) - - [useDocumentOnce](#usedocumentonce) - - [useDocumentData](#usedocumentdata) - - [useDocumentDataOnce](#usedocumentdataonce) + - [useCollection](#usecollection) + - [Full example](#full-example) + - [useCollectionOnce](#usecollectiononce) + - [useCollectionData](#usecollectiondata) + - [useCollectionDataOnce](#usecollectiondataonce) + - [useDocument](#usedocument) + - [Full example](#full-example-1) + - [useDocumentOnce](#usedocumentonce) + - [useDocumentData](#usedocumentdata) + - [useDocumentDataOnce](#usedocumentdataonce) - [Transforming data](#transforming-data) Additional functionality: @@ -115,7 +115,7 @@ Returns: ### useCollectionData ```js -const [values, loading, error] = useCollectionData (query, options); +const [values, loading, error] = useCollectionData < T > (query, options); ``` As `useCollection`, but this hook extracts a typed list of the `firestore.QuerySnapshot.docs` values, rather than the @@ -125,11 +125,8 @@ The `useCollectionData` hook takes the following parameters: - `query`: (optional) `firestore.Query` for the data you would like to load - `options`: (optional) `Object` with the following parameters: - - `idField`: (optional) name of the field that should be populated with the `firestore.QuerySnapshot.id` property. - - `refField`: (optional) name of the field that should be populated with the `firestore.QuerySnapshot.ref` property. - `snapshotListenOptions`: (optional) `firestore.SnapshotListenOptions` to customise how the collection is loaded - `snapshotOptions`: (optional) `firestore.SnapshotOptions` to customise how data is retrieved from snapshots - - `transform`: (optional) a function that receives the raw `firestore.DocumentData` for each item in the collection to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below. Returns: @@ -140,7 +137,7 @@ Returns: ### useCollectionDataOnce ```js -const [values, loading, error] = useCollectionDataOnce(query, options); +const [values, loading, error] = useCollectionDataOnce < T > (query, options); ``` As `useCollectionData`, but this hook will only read the current value of the `firestore.Query`. @@ -151,10 +148,7 @@ The `useCollectionDataOnce` hook takes the following parameters: - `options`: (optional) `Object` with the following parameters: - `getOptions`: (optional) `Object` to customise how the collection is loaded - `source`: (optional): `'default' | 'server' | 'cache'` Describes whether we should get from server or cache. - - `idField`: (optional) name of the field that should be populated with the `firestore.QuerySnapshot.id` property. - - `refField`: (optional) name of the field that should be populated with the `firestore.QuerySnapshot.ref` property. - `snapshotOptions`: (optional) `firestore.SnapshotOptions` to customise how data is retrieved from snapshots - - `transform`: (optional) a function that receives the raw `firestore.DocumentData` for each item in the collection to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below. Returns: @@ -231,7 +225,7 @@ Returns: ### useDocumentData ```js -const [value, loading, error] = useDocumentData(reference, options); +const [value, loading, error] = useDocumentData < T > (reference, options); ``` As `useDocument`, but this hook extracts the typed contents of `firestore.DocumentSnapshot.data()`, rather than the @@ -241,11 +235,8 @@ The `useDocumentData` hook takes the following parameters: - `reference`: (optional) `firestore.DocumentReference` for the data you would like to load - `options`: (optional) `Object` with the following parameters: - - `idField`: (optional) name of the field that should be populated with the `firestore.DocumentSnapshot.id` property. - - `refField`: (optional) name of the field that should be populated with the `firestore.QuerySnapshot.ref` property. - `snapshotListenOptions`: (optional) `firestore.SnapshotListenOptions` to customise how the collection is loaded - `snapshotOptions`: (optional) `firestore.SnapshotOptions` to customise how data is retrieved from snapshots - - `transform`: (optional) a function that receives the raw `firestore.DocumentData` to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below. Returns: @@ -256,7 +247,7 @@ Returns: ### useDocumentDataOnce ```js -const [value, loading, error] = useDocumentDataOnce (reference, options); +const [value, loading, error] = useDocumentDataOnce < T > (reference, options); ``` As `useDocument`, but this hook will only read the current value of the `firestore.DocumentReference`. @@ -266,11 +257,8 @@ The `useDocumentDataOnce` hook takes the following parameters: - `reference`: (optional) `firestore.DocumentReference` for the data you would like to load - `options`: (optional) `Object` with the following parameters: - `getOptions`: (optional) `Object` to customise how the collection is loaded - - `source`: (optional): `'default' | 'server' | 'cache'` Describes whether we should get from server or cache. - - `idField`: (optional) name of the field that should be populated with the `firestore.DocumentSnapshot.id` property. - - `refField`: (optional) name of the field that should be populated with the `firestore.QuerySnapshot.ref` property. + - `source`: (optional): `'default' | 'server' | 'cache'` Describes whether we should get from server or cach - `snapshotOptions`: (optional) `firestore.SnapshotOptions` to customise how data is retrieved from snapshots - - `transform`: (optional) a function that receives the raw `firestore.DocumentData` to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below. Returns: @@ -280,14 +268,6 @@ Returns: ## Transforming data -Firestore allows a restricted number of data types in its store, which may not be flexible enough for your application. Both `useCollectionData` and `useDocumentData` support an optional `transform` function which allows the transformation of the underlying Firestore data into whatever format the application requires, e.g. a `Date` type. - -```js -transform?: (val: any) => T; -``` - -The `transform` function is passed a single row of a data, so will be called once when used with `useDocumentData` and multiple times when used with `useCollectionData`. - -The `transform` function will not receive the `id` or `ref` values referenced in the properties named in the `idField` or `refField` options, nor it is expected to produce them. Either or both, if specified, will be merged afterwards. +Firestore allows a restricted number of data types in its store, which may not be flexible enough for your application. As of Firebase 9, there is a built in FirestoreDataConverter which allows you to transform data as it leaves the Firestore database. This is described here: https://firebase.google.com/docs/reference/js/firestore_.firestoredataconverter -If the `transform` function is defined within your React component, it is recomended that you memoize the function to prevent unnecessry renders. +> This has replaced the `transform`, `idField` and `refField` options that were available in `react-firebase-hooks` v4 and earlier. diff --git a/firestore/helpers/index.ts b/firestore/helpers/index.ts index 9818d04..d803d9f 100644 --- a/firestore/helpers/index.ts +++ b/firestore/helpers/index.ts @@ -1,40 +1,12 @@ import { CollectionReference, - DocumentData, DocumentReference, - DocumentSnapshot, Query, queryEqual, refEqual, - SnapshotOptions, } from 'firebase/firestore'; import { RefHook, useComparatorRef } from '../../util'; -export const snapshotToData = ( - snapshot: DocumentSnapshot, - snapshotOptions?: SnapshotOptions, - idField?: string, - refField?: string, - transform?: (val: any) => T -) => { - if (!snapshot.exists()) { - return undefined; - } - - let data = snapshot.data(snapshotOptions) as DocumentData; - if (transform) { - data = transform(data); - } - if (idField) { - data[idField] = snapshot.id; - } - if (refField) { - data[refField] = snapshot.ref; - } - - return data; -}; - const isRefEqual = < T extends DocumentReference | CollectionReference >( diff --git a/firestore/types.ts b/firestore/types.ts index da1e2d8..a3cf937 100644 --- a/firestore/types.ts +++ b/firestore/types.ts @@ -10,10 +10,7 @@ import { import { LoadingHook } from '../util'; export type IDOptions = { - idField?: string; - refField?: string; snapshotOptions?: SnapshotOptions; - transform?: (val: any) => T; }; export type Options = { snapshotListenOptions?: SnapshotListenOptions; @@ -26,28 +23,18 @@ export type GetOptions = { source?: 'default' | 'server' | 'cache'; }; export type OnceDataOptions = OnceOptions & IDOptions; -export type Data< - T = DocumentData, - IDField extends string = '', - RefField extends string = '' -> = T & Record & Record>; export type CollectionHook = LoadingHook< QuerySnapshot, FirestoreError >; -export type CollectionDataHook< - T = DocumentData, - IDField extends string = '', - RefField extends string = '' -> = LoadingHook[], FirestoreError>; +export type CollectionDataHook = LoadingHook< + T[], + FirestoreError +>; export type DocumentHook = LoadingHook< DocumentSnapshot, FirestoreError >; -export type DocumentDataHook< - T = DocumentData, - IDField extends string = '', - RefField extends string = '' -> = LoadingHook, FirestoreError>; +export type DocumentDataHook = LoadingHook; diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index 98810be..7229427 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -10,11 +10,10 @@ import { } from 'firebase/firestore'; import { useEffect, useMemo } from 'react'; import { useLoadingValue } from '../util'; -import { snapshotToData, useIsFirestoreQueryEqual } from './helpers'; +import { useIsFirestoreQueryEqual } from './helpers'; import { CollectionDataHook, CollectionHook, - Data, DataOptions, GetOptions, OnceDataOptions, @@ -36,26 +35,18 @@ export const useCollectionOnce = ( return useCollectionInternal(false, query, options); }; -export const useCollectionData = < - T = DocumentData, - IDField extends string = '', - RefField extends string = '' ->( +export const useCollectionData = ( query?: Query | null, options?: DataOptions -): CollectionDataHook => { - return useCollectionDataInternal(true, query, options); +): CollectionDataHook => { + return useCollectionDataInternal(true, query, options); }; -export const useCollectionDataOnce = < - T = DocumentData, - IDField extends string = '', - RefField extends string = '' ->( +export const useCollectionDataOnce = ( query?: Query | null, options?: OnceDataOptions -): CollectionDataHook => { - return useCollectionDataInternal(false, query, options); +): CollectionDataHook => { + return useCollectionDataInternal(false, query, options); }; const useCollectionInternal = ( @@ -104,19 +95,12 @@ const useCollectionInternal = ( return useMemo(() => resArray, resArray); }; -const useCollectionDataInternal = < - T = DocumentData, - IDField extends string = '', - RefField extends string = '' ->( +const useCollectionDataInternal = ( listen: boolean, query?: Query | null, options?: DataOptions & OnceDataOptions -): CollectionDataHook => { - const idField = options ? options.idField : undefined; - const refField = options ? options.refField : undefined; +): CollectionDataHook => { const snapshotOptions = options ? options.snapshotOptions : undefined; - const transform = options ? options.transform : undefined; const [snapshots, loading, error] = useCollectionInternal( listen, query, @@ -125,24 +109,12 @@ const useCollectionDataInternal = < const values = useMemo( () => (snapshots - ? snapshots.docs.map((doc) => - snapshotToData( - doc, - snapshotOptions, - idField, - refField, - transform - ) - ) - : undefined) as Data[], - [snapshots, snapshotOptions, idField, refField, transform] + ? snapshots.docs.map((doc) => doc.data(snapshotOptions)) + : undefined) as T[], + [snapshots, snapshotOptions] ); - const resArray: CollectionDataHook = [ - values, - loading, - error, - ]; + const resArray: CollectionDataHook = [values, loading, error]; return useMemo(() => resArray, resArray); }; diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index a9a3551..2013bef 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -10,9 +10,8 @@ import { } from 'firebase/firestore'; import { useEffect, useMemo } from 'react'; import { useLoadingValue } from '../util'; -import { snapshotToData, useIsFirestoreRefEqual } from './helpers'; +import { useIsFirestoreRefEqual } from './helpers'; import { - Data, DataOptions, DocumentDataHook, DocumentHook, @@ -35,26 +34,18 @@ export const useDocumentOnce = ( return useDocumentInternal(false, docRef, options); }; -export const useDocumentData = < - T = DocumentData, - IDField extends string = '', - RefField extends string = '' ->( +export const useDocumentData = ( docRef?: DocumentReference | null, options?: DataOptions -): DocumentDataHook => { - return useDocumentDataInternal(true, docRef, options); +): DocumentDataHook => { + return useDocumentDataInternal(true, docRef, options); }; -export const useDocumentDataOnce = < - T = DocumentData, - IDField extends string = '', - RefField extends string = '' ->( +export const useDocumentDataOnce = ( docRef?: DocumentReference | null, options?: OnceDataOptions -): DocumentDataHook => { - return useDocumentDataInternal(false, docRef, options); +): DocumentDataHook => { + return useDocumentDataInternal(false, docRef, options); }; const useDocumentInternal = ( @@ -104,43 +95,23 @@ const useDocumentInternal = ( return useMemo(() => resArray, resArray); }; -const useDocumentDataInternal = < - T = DocumentData, - IDField extends string = '', - RefField extends string = '' ->( +const useDocumentDataInternal = ( listen: boolean, docRef?: DocumentReference | null, options?: DataOptions -): DocumentDataHook => { - const idField = options ? options.idField : undefined; - const refField = options ? options.refField : undefined; +): DocumentDataHook => { const snapshotOptions = options ? options.snapshotOptions : undefined; - const transform = options ? options.transform : undefined; const [snapshot, loading, error] = useDocumentInternal( listen, docRef, options ); const value = useMemo( - () => - (snapshot - ? snapshotToData( - snapshot, - snapshotOptions, - idField, - refField, - transform - ) - : undefined) as Data, - [snapshot, snapshotOptions, idField, refField, transform] + () => (snapshot ? snapshot.data(snapshotOptions) : undefined) as T, + [snapshot, snapshotOptions] ); - const resArray: DocumentDataHook = [ - value, - loading, - error, - ]; + const resArray: DocumentDataHook = [value, loading, error]; return useMemo(() => resArray, resArray); }; From 428ee680184e0d96d82cb80403e5e57d7638cf9b Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 21 Dec 2021 16:53:27 +0000 Subject: [PATCH 09/29] [firestore] Syntax tidy up --- firestore/useCollection.ts | 36 +++++++++++++++--------------------- firestore/useDocument.ts | 37 +++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index 7229427..eafa2db 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -53,7 +53,7 @@ const useCollectionInternal = ( listen: boolean, query?: Query | null, options?: Options & OnceOptions -) => { +): CollectionHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< QuerySnapshot, FirestoreError @@ -66,23 +66,20 @@ const useCollectionInternal = ( return; } if (listen) { - const listener = - options && options.snapshotListenOptions - ? onSnapshot( - ref.current, - options.snapshotListenOptions, - setValue, - setError - ) - : onSnapshot(ref.current, setValue, setError); + const listener = options?.snapshotListenOptions + ? onSnapshot( + ref.current, + options.snapshotListenOptions, + setValue, + setError + ) + : onSnapshot(ref.current, setValue, setError); return () => { listener(); }; } else { - const get = getDocsFnFromGetOptions( - options ? options.getOptions : undefined - ); + const get = getDocsFnFromGetOptions(options?.getOptions); get(ref.current).then(setValue).catch(setError); } }, [ref.current]); @@ -100,17 +97,14 @@ const useCollectionDataInternal = ( query?: Query | null, options?: DataOptions & OnceDataOptions ): CollectionDataHook => { - const snapshotOptions = options ? options.snapshotOptions : undefined; + const snapshotOptions = options?.snapshotOptions; const [snapshots, loading, error] = useCollectionInternal( listen, query, options ); const values = useMemo( - () => - (snapshots - ? snapshots.docs.map((doc) => doc.data(snapshotOptions)) - : undefined) as T[], + () => snapshots?.docs.map((doc) => doc.data(snapshotOptions)) as T[], [snapshots, snapshotOptions] ); @@ -118,9 +112,9 @@ const useCollectionDataInternal = ( return useMemo(() => resArray, resArray); }; -function getDocsFnFromGetOptions( +const getDocsFnFromGetOptions = ( { source }: GetOptions = { source: 'default' } -) { +) => { switch (source) { default: case 'default': @@ -130,4 +124,4 @@ function getDocsFnFromGetOptions( case 'server': return getDocsFromServer; } -} +}; diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index 2013bef..a4b286f 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -65,23 +65,20 @@ const useDocumentInternal = ( return; } if (listen) { - const listener = - options && options.snapshotListenOptions - ? onSnapshot( - ref.current, - options.snapshotListenOptions, - setValue, - setError - ) - : onSnapshot(ref.current, setValue, setError); + const listener = options?.snapshotListenOptions + ? onSnapshot( + ref.current, + options.snapshotListenOptions, + setValue, + setError + ) + : onSnapshot(ref.current, setValue, setError); return () => { listener(); }; } else { - const get = getDocFnFromGetOptions( - options ? options.getOptions : undefined - ); + const get = getDocFnFromGetOptions(options?.getOptions); get(ref.current).then(setValue).catch(setError); } @@ -100,24 +97,24 @@ const useDocumentDataInternal = ( docRef?: DocumentReference | null, options?: DataOptions ): DocumentDataHook => { - const snapshotOptions = options ? options.snapshotOptions : undefined; + const snapshotOptions = options?.snapshotOptions; const [snapshot, loading, error] = useDocumentInternal( listen, docRef, options ); - const value = useMemo( - () => (snapshot ? snapshot.data(snapshotOptions) : undefined) as T, - [snapshot, snapshotOptions] - ); + const value = useMemo(() => snapshot?.data(snapshotOptions) as T, [ + snapshot, + snapshotOptions, + ]); const resArray: DocumentDataHook = [value, loading, error]; return useMemo(() => resArray, resArray); }; -function getDocFnFromGetOptions( +const getDocFnFromGetOptions = ( { source }: GetOptions = { source: 'default' } -) { +) => { switch (source) { default: case 'default': @@ -127,4 +124,4 @@ function getDocFnFromGetOptions( case 'server': return getDocFromServer; } -} +}; From 8bfc5d2ea15358c53c216cb80275906b4cae14f1 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 4 Jan 2022 14:32:44 +0000 Subject: [PATCH 10/29] Add documentation for `useAuthState` options --- auth/README.md | 8 +++++--- auth/useAuthState.ts | 32 ++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/auth/README.md b/auth/README.md index dc087e5..01c909c 100644 --- a/auth/README.md +++ b/auth/README.md @@ -17,7 +17,7 @@ List of Auth hooks: ### useAuthState ```js -const [user, loading, error] = useAuthState(auth); +const [user, loading, error] = useAuthState(auth, options); ``` Retrieve and monitor the authentication state from Firebase. @@ -25,6 +25,8 @@ Retrieve and monitor the authentication state from Firebase. The `useAuthState` hook takes the following parameters: - `auth`: `auth.Auth` instance for the app you would like to monitor +- `options`: (optional) `Object with the following parameters: + - `onUserChanged`: (optional) function to be called with `auth.User` each time the user changes. This allows you to do things like load custom claims. Returns: @@ -40,13 +42,13 @@ Returns: import { getAuth, signInWithEmailAndPassword, signOut } from 'firebase/auth'; import { useAuthState } from 'react-firebase-hooks/auth'; -const auth = getAuth(firebaseApp) +const auth = getAuth(firebaseApp); const login = () => { signInWithEmailAndPassword(auth, 'test@test.com', 'password'); }; const logout = () => { - signOut(auth) + signOut(auth); }; const CurrentUser = () => { diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 6d2a2a8..8de83b7 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -4,24 +4,32 @@ import { LoadingHook, useLoadingValue } from '../util'; export type AuthStateHook = LoadingHook; -export default (auth: firebase.auth.Auth, options: {onUserChanged?: (user?: firebase.User) => Promise}): AuthStateHook => { +type AuthStateOptions = { + onUserChanged?: (user: User | null) => Promise; +}; + +export default (auth: Auth, options?: AuthStateOptions): AuthStateHook => { const { error, loading, setError, setValue, value } = useLoadingValue< User | null, Error >(() => auth.currentUser); useEffect(() => { - const listener = auth.onAuthStateChanged(async (user) => { - if(typeof options?.onUserChanged === 'function') { - // onUserLoaded function to process custom claims on any other trigger function - try { - await options.onUserChanged(user) - } catch(e) { - setError(e) - } - } - setValue(user); - }, setError); + const listener = onAuthStateChanged( + auth, + async (user) => { + if (options?.onUserChanged) { + // onUserLoaded function to process custom claims on any other trigger function + try { + await options.onUserChanged(user); + } catch (e) { + setError(e as Error); + } + } + setValue(user); + }, + setError + ); return () => { listener(); From ae553b9a9e1ecabdf2c81289e754eacd7b3c183a Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 4 Jan 2022 14:59:48 +0000 Subject: [PATCH 11/29] Add documentation and tweak `useSignInWithGoogle` --- auth/README.md | 63 +++++++++++++++++++++++++++++++++++++ auth/useSignInWithGoogle.ts | 42 +++++++++++++++---------- 2 files changed, 89 insertions(+), 16 deletions(-) diff --git a/auth/README.md b/auth/README.md index 01c909c..d1f2cff 100644 --- a/auth/README.md +++ b/auth/README.md @@ -13,6 +13,7 @@ List of Auth hooks: - [useAuthState](#useauthstate) - [useCreateUserWithEmailAndPassword](#usecreateuserwithemailandpassword) - [useSignInWithEmailAndPassword](#usesigninwithemailandpassword) +- [useSignInWithGoogle](#usesigninwithgoogle) ### useAuthState @@ -234,3 +235,65 @@ const SignIn = () => { ); }; ``` + +### useSignInWithGoogle + +```js +const [signInWithGoogle, user, loading, error] = useSignInWithGoogle(auth); +``` + +Login a user with Google Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.GoogleProvider` and provides additional `loading` and `error` information. + +The `useSignInWithGoogle` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithGoogle(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useSignInWithGoogle } from 'react-firebase-hooks/auth'; + +const SignIn = () => { + const [signInWithGoogle, user, loading, error] = useSignInWithGoogle(auth); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (loading) { + return

Loading...

; + } + if (user) { + return ( +
+

Signed In User: {user.email}

+
+ ); + } + return ( +
+ setEmail(e.target.value)} + /> + setPassword(e.target.value)} + /> + +
+ ); +}; +``` diff --git a/auth/useSignInWithGoogle.ts b/auth/useSignInWithGoogle.ts index b25cd05..64cedd3 100644 --- a/auth/useSignInWithGoogle.ts +++ b/auth/useSignInWithGoogle.ts @@ -1,27 +1,37 @@ import { useState, useMemo } from 'react'; -import firebase from 'firebase/app'; +import { + Auth, + AuthError, + CustomParameters, + GoogleAuthProvider, + signInWithPopup, + UserCredential, +} from 'firebase/auth'; import { GoogleActionHook } from './types'; -export default ( - auth: firebase.auth.Auth, - extraScopes: Array = [] -): GoogleActionHook => { - const [error, setError] = useState(); - const [ - loggedInUser, - setLoggedInUser, - ] = useState(); +export default (auth: Auth): GoogleActionHook => { + const [error, setError] = useState(); + const [loggedInUser, setLoggedInUser] = useState(); const [loading, setLoading] = useState(false); - const signInWithGoogle = async () => { + const signInWithGoogle = async ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { setLoading(true); try { - const provider = new firebase.auth.GoogleAuthProvider(); - extraScopes.forEach((extraScope) => provider.addScope(extraScope)); - await auth.signInWithPopup(provider).then(setLoggedInUser); - setLoading(false); + const provider = new GoogleAuthProvider(); + if (scopes) { + scopes.forEach((scope) => provider.addScope(scope)); + } + if (customOAuthParameters) { + provider.setCustomParameters(customOAuthParameters); + } + const user = await signInWithPopup(auth, provider); + setLoggedInUser(user); } catch (err) { - setError(err); + setError(err as AuthError); + } finally { setLoading(false); } }; From bccbe7794a7cb2e88aba1ecbf437edde2f02b61e Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 4 Jan 2022 15:38:43 +0000 Subject: [PATCH 12/29] Add additional social login providers --- auth/README.md | 158 +++++++++++++++++++++++++++++++++++- auth/index.ts | 9 ++ auth/types.ts | 27 +++--- auth/useSignInWithGoogle.ts | 46 ----------- auth/useSignInWithPopup.ts | 151 ++++++++++++++++++++++++++++++++++ 5 files changed, 328 insertions(+), 63 deletions(-) delete mode 100644 auth/useSignInWithGoogle.ts create mode 100644 auth/useSignInWithPopup.ts diff --git a/auth/README.md b/auth/README.md index d1f2cff..9aafe8d 100644 --- a/auth/README.md +++ b/auth/README.md @@ -13,7 +13,13 @@ List of Auth hooks: - [useAuthState](#useauthstate) - [useCreateUserWithEmailAndPassword](#usecreateuserwithemailandpassword) - [useSignInWithEmailAndPassword](#usesigninwithemailandpassword) +- [useSignInWithApple](#usesigninwithapple) +- [useSignInWithFacebook](#usesigninwithfacebook) +- [useSignInWithGithub](#usesigninwithgithub) - [useSignInWithGoogle](#usesigninwithgoogle) +- [useSignInWithMicrosoft](#usesigninwithmicrosoft) +- [useSignInWithTwitter](#usesigninwithtwitter) +- [useSignInWithYahoo](#usesigninwithyahoo) ### useAuthState @@ -236,6 +242,75 @@ const SignIn = () => { }; ``` +### useSignInWithApple + +```js +const [signInWithApple, user, loading, error] = useSignInWithApple(auth); +``` + +Login a user with Apple Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.OAuthProvider` and provides additional `loading` and `error` information. + +The `useSignInWithApple` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithApple(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full example + +See [social login example](#social-login-example) + +### useSignInWithFacebook + +```js +const [signInWithFacebook, user, loading, error] = useSignInWithFacebook(auth); +``` + +Login a user with Facebook Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.OAuthProvider` and provides additional `loading` and `error` information. + +The `useSignInWithApple` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithFacebook(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full example + +See [social login example](#social-login-example) + +### useSignInWithGithub + +```js +const [signInWithGithub, user, loading, error] = useSignInWithGithub(auth); +``` + +Login a user with Github Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.OAuthProvider` and provides additional `loading` and `error` information. + +The `useSignInWithGithub` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithGithub(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full example + +See [social login example](#social-login-example) + ### useSignInWithGoogle ```js @@ -255,13 +330,88 @@ Returns: - `loading`: A `boolean` to indicate whether the the user login is processing - `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error -#### Full Example +#### Full example + +See [social login example](#social-login-example) + +### useSignInWithMicrosoft + +```js +const [signInWithMicrosoft, user, loading, error] = useSignInWithMicrosoft( + auth +); +``` + +Login a user with Microsoftt Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.OAuthProvider` and provides additional `loading` and `error` information. + +The `useSignInWithMicrosoft` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithMicrosoft(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full example + +See [social login example](#social-login-example) + +### useSignInWithTwittter + +```js +const [signInWithTwitter, user, loading, error] = useSignInWithTwitter(auth); +``` + +Login a user with Twitter Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.OAuthProvider` and provides additional `loading` and `error` information. + +The `useSignInWithTwitter` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithTwitter(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full example + +See [social login example](#social-login-example) + +### useSignInWithYahoo + +```js +const [signInWithYahoo, user, loading, error] = useSignInWithYahoo(auth); +``` + +Login a user with Yahoo Authenticatiton. Wraps the underlying `auth.signInWithPopup` method with the `auth.OAuthProvider` and provides additional `loading` and `error` information. + +The `useSignInWithYahoo` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `signInWithYahoo(scopes: string[], customOAuthParameters: auth.CustomParameters)`: a function you can call to start the login +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full example + +See [social login example](#social-login-example) + +### Social Login Example ```jsx -import { useSignInWithGoogle } from 'react-firebase-hooks/auth'; +import { useSignInWithXXX } from 'react-firebase-hooks/auth'; const SignIn = () => { - const [signInWithGoogle, user, loading, error] = useSignInWithGoogle(auth); + const [signInWithXXX, user, loading, error] = useSignInWithXXX(auth); if (error) { return ( @@ -292,7 +442,7 @@ const SignIn = () => { value={password} onChange={(e) => setPassword(e.target.value)} /> - + ); }; diff --git a/auth/index.ts b/auth/index.ts index 7d97376..0cdb489 100644 --- a/auth/index.ts +++ b/auth/index.ts @@ -1,5 +1,14 @@ export { default as useAuthState, AuthStateHook } from './useAuthState'; export { default as useSignInWithEmailAndPassword } from './useSignInWithEmailAndPassword'; +export { + useSignInWithApple, + useSignInWithFacebook, + useSignInWithGithub, + useSignInWithGoogle, + useSignInWithMicrosoft, + useSignInWithTwitter, + useSignInWithYahoo, +} from './useSignInWithPopup'; export { default as useCreateUserWithEmailAndPassword } from './useCreateUserWithEmailAndPassword'; export { EmailAndPasswordActionHook } from './types'; diff --git a/auth/types.ts b/auth/types.ts index d8b3504..7c8f8cd 100644 --- a/auth/types.ts +++ b/auth/types.ts @@ -1,23 +1,24 @@ -import { ActionCodeSettings, AuthError, UserCredential } from 'firebase/auth'; +import { + ActionCodeSettings, + AuthError, + CustomParameters, + UserCredential, +} from 'firebase/auth'; -export type AuthActionHook = [ - (email: string, password: string) => Promise, - T | undefined, +export type AuthActionHook = [ + M, + UserCredential | undefined, boolean, - E | undefined + AuthError | undefined ]; export type CreateUserOptions = { emailVerificationOptions?: ActionCodeSettings; sendEmailVerification?: boolean; }; export type EmailAndPasswordActionHook = AuthActionHook< - UserCredential, - AuthError + (email: string, password: string) => Promise >; -export type GoogleActionHook = [ - () => void, - firebase.auth.UserCredential | undefined, - boolean, - firebase.FirebaseError | undefined -]; +export type SignInWithPopupHook = AuthActionHook< + (scopes?: string[], customOAuthParameters?: CustomParameters) => Promise +>; diff --git a/auth/useSignInWithGoogle.ts b/auth/useSignInWithGoogle.ts deleted file mode 100644 index 64cedd3..0000000 --- a/auth/useSignInWithGoogle.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { useState, useMemo } from 'react'; -import { - Auth, - AuthError, - CustomParameters, - GoogleAuthProvider, - signInWithPopup, - UserCredential, -} from 'firebase/auth'; -import { GoogleActionHook } from './types'; - -export default (auth: Auth): GoogleActionHook => { - const [error, setError] = useState(); - const [loggedInUser, setLoggedInUser] = useState(); - const [loading, setLoading] = useState(false); - - const signInWithGoogle = async ( - scopes?: string[], - customOAuthParameters?: CustomParameters - ) => { - setLoading(true); - try { - const provider = new GoogleAuthProvider(); - if (scopes) { - scopes.forEach((scope) => provider.addScope(scope)); - } - if (customOAuthParameters) { - provider.setCustomParameters(customOAuthParameters); - } - const user = await signInWithPopup(auth, provider); - setLoggedInUser(user); - } catch (err) { - setError(err as AuthError); - } finally { - setLoading(false); - } - }; - - const resArray: GoogleActionHook = [ - signInWithGoogle, - loggedInUser, - loading, - error, - ]; - return useMemo(() => resArray, resArray); -}; diff --git a/auth/useSignInWithPopup.ts b/auth/useSignInWithPopup.ts new file mode 100644 index 0000000..a7c6bf5 --- /dev/null +++ b/auth/useSignInWithPopup.ts @@ -0,0 +1,151 @@ +import { useState, useMemo } from 'react'; +import { + Auth, + AuthError, + AuthProvider, + CustomParameters, + FacebookAuthProvider, + GithubAuthProvider, + GoogleAuthProvider, + OAuthProvider, + TwitterAuthProvider, + signInWithPopup, + UserCredential, +} from 'firebase/auth'; +import { SignInWithPopupHook } from './types'; + +export const useSignInWithApple = (auth: Auth): SignInWithPopupHook => { + return useSignInWithOAuth(auth, 'apple.com'); +}; + +export const useSignInWithFacebook = (auth: Auth): SignInWithPopupHook => { + const createFacebookAuthProvider = ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { + const provider = new FacebookAuthProvider(); + if (scopes) { + scopes.forEach((scope) => provider.addScope(scope)); + } + if (customOAuthParameters) { + provider.setCustomParameters(customOAuthParameters); + } + return provider; + }; + return useSignInWithPopup(auth, createFacebookAuthProvider); +}; + +export const useSignInWithGithub = (auth: Auth): SignInWithPopupHook => { + const createGithubAuthProvider = ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { + const provider = new GithubAuthProvider(); + if (scopes) { + scopes.forEach((scope) => provider.addScope(scope)); + } + if (customOAuthParameters) { + provider.setCustomParameters(customOAuthParameters); + } + return provider; + }; + return useSignInWithPopup(auth, createGithubAuthProvider); +}; + +export const useSignInWithGoogle = (auth: Auth): SignInWithPopupHook => { + const createGoogleAuthProvider = ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { + const provider = new GoogleAuthProvider(); + if (scopes) { + scopes.forEach((scope) => provider.addScope(scope)); + } + if (customOAuthParameters) { + provider.setCustomParameters(customOAuthParameters); + } + return provider; + }; + return useSignInWithPopup(auth, createGoogleAuthProvider); +}; + +export const useSignInWithMicrosoft = (auth: Auth): SignInWithPopupHook => { + return useSignInWithOAuth(auth, 'microsoft.com'); +}; + +export const useSignInWithTwitter = (auth: Auth): SignInWithPopupHook => { + const createTwitterAuthProvider = ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { + const provider = new TwitterAuthProvider(); + if (scopes) { + scopes.forEach((scope) => provider.addScope(scope)); + } + if (customOAuthParameters) { + provider.setCustomParameters(customOAuthParameters); + } + return provider; + }; + return useSignInWithPopup(auth, createTwitterAuthProvider); +}; + +export const useSignInWithYahoo = (auth: Auth): SignInWithPopupHook => { + return useSignInWithOAuth(auth, 'yahoo.com'); +}; + +const useSignInWithOAuth = ( + auth: Auth, + providerId: string +): SignInWithPopupHook => { + const createOAuthProvider = ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { + const provider = new OAuthProvider(providerId); + if (scopes) { + scopes.forEach((scope) => provider.addScope(scope)); + } + if (customOAuthParameters) { + provider.setCustomParameters(customOAuthParameters); + } + return provider; + }; + return useSignInWithPopup(auth, createOAuthProvider); +}; + +const useSignInWithPopup = ( + auth: Auth, + createProvider: ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => AuthProvider +): SignInWithPopupHook => { + const [error, setError] = useState(); + const [loggedInUser, setLoggedInUser] = useState(); + const [loading, setLoading] = useState(false); + + const signInWithGoogle = async ( + scopes?: string[], + customOAuthParameters?: CustomParameters + ) => { + setLoading(true); + try { + const provider = createProvider(scopes, customOAuthParameters); + const user = await signInWithPopup(auth, provider); + setLoggedInUser(user); + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }; + + const resArray: SignInWithPopupHook = [ + signInWithGoogle, + loggedInUser, + loading, + error, + ]; + return useMemo(() => resArray, resArray); +}; From 08489c5de26b366669149fc98df449527fe59df6 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 4 Jan 2022 18:59:18 +0000 Subject: [PATCH 13/29] [auth] Add user update and email sending methods --- auth/README.md | 297 ++++++++++++++++++++++++++++++ auth/index.ts | 20 +- auth/useSendEmailVerification.ts | 39 ++++ auth/useSendPasswordResetEmail.ts | 39 ++++ auth/useUpdateUser.ts | 92 +++++++++ 5 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 auth/useSendEmailVerification.ts create mode 100644 auth/useSendPasswordResetEmail.ts create mode 100644 auth/useUpdateUser.ts diff --git a/auth/README.md b/auth/README.md index 9aafe8d..66f7451 100644 --- a/auth/README.md +++ b/auth/README.md @@ -20,6 +20,11 @@ List of Auth hooks: - [useSignInWithMicrosoft](#usesigninwithmicrosoft) - [useSignInWithTwitter](#usesigninwithtwitter) - [useSignInWithYahoo](#usesigninwithyahoo) +- [useUpdateEmail](#useupdateemail) +- [useUpdatePassword](#useupdatepassword) +- [useUpdateProfile](#useupdateprofile) +- [useSendPasswordResetEmaili](#usesendpasswordresetemail) +- [useSendEmailVerification](#usesendemailverification) ### useAuthState @@ -447,3 +452,295 @@ const SignIn = () => { ); }; ``` + +### useUpdateEmail + +```js +const [updateEmail, updating, error] = useUpdateEmail(auth); +``` + +Update the current user's email address. Wraps the underlying `auth.updateEmail` method and provides additional `updating` and `error` information. + +The `useUpdateEmail` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `updateEmail(email: string)`: a function you can call to update the current user's email addres +- `updating`: A `boolean` to indicate whether the user update is processing +- `error`: Any `Error` returned by Firebase when trying to update the user, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useUpdateEmail } from 'react-firebase-hooks/auth'; + +const UpdateEmail = () => { + const [email, setEmail] = useState(''); + const [updateEmail, updating, error] = useUpdateEmail(auth); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (updating) { + return

Updating...

; + } + return ( +
+ setEmail(e.target.value)} + /> + +
+ ); +}; +``` + +### useUpdatePassword + +```js +const [updatePassword, updating, error] = useUpdatePassword(auth); +``` + +Update the current user's password. Wraps the underlying `auth.updatePassword` method and provides additional `updating` and `error` information. + +The `useUpdatePassword` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `updatePassword(password: string)`: a function you can call to update the current user's password +- `updating`: A `boolean` to indicate whether the user update is processing +- `error`: Any `Error` returned by Firebase when trying to update the user, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useUpdatePassword } from 'react-firebase-hooks/auth'; + +const UpdatePassword = () => { + const [password, setPassword] = useState(''); + const [updatePassword, updating, error] = useUpdatePassword(auth); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (updating) { + return

Updating...

; + } + return ( +
+ setPassword(e.target.value)} + /> + +
+ ); +}; +``` + +### useUpdateProfile + +```js +const [updateProfile, updating, error] = useUpdateProfile(auth); +``` + +Update the current user's profile. Wraps the underlying `auth.updateProfile` method and provides additional `updating` and `error` information. + +The `useUpdateProfile` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `updateProfile({ displayName: string, photoURL: string })`: a function you can call to update the current user's profile +- `updating`: A `boolean` to indicate whether the user update is processing +- `error`: Any `Error` returned by Firebase when trying to update the user, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useUpdateProfile } from 'react-firebase-hooks/auth'; + +const UpdateProfile = () => { + const [displayName, setDisplayName] = useState(''); + const [photoURL, setPhotoURL] = useState(''); + const [updateProfile, updating, error] = useUpdateProfile(auth); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (updating) { + return

Updating...

; + } + return ( +
+ setDisplayName(e.target.value)} + /> + setPhotoURL(e.target.value)} + /> + +
+ ); +}; +``` + +### useSendPasswordResetEmail + +```js +const [sendPasswordResetEmail, sending, error] = useSendPasswordResetEmail( + auth +); +``` + +Send a password reset email to the specified email address. Wraps the underlying `auth.sendPasswordResetEmail` method and provides additional `sending` and `error` information. + +The `useSendPasswordResetEmail` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `sendPasswordResetEmail(email: string)`: a function you can call to send a password reset emaail +- `sending`: A `boolean` to indicate whether the email is being sent +- `error`: Any `Error` returned by Firebase when trying to send the email, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useSendPasswordResetEmail } from 'react-firebase-hooks/auth'; + +const SendPasswordReset = () => { + const [email, setEmail] = useState(''); + const [sendPasswordResetEmail, sending, error] = useSendPasswordResetEmail( + auth + ); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (sending) { + return

Sending...

; + } + return ( +
+ setEmail(e.target.value)} + /> + +
+ ); +}; +``` + +### useSendEmailVerification + +```js +const [sendEmailVerification, sending, error] = useSendEmailVerification(auth); +``` + +Send a verification email to the current user. Wraps the underlying `auth.sendEmailVerification` method and provides additional `sending` and `error` information. + +The `useSendEmailVerification` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `sendEmailVerification()`: a function you can call to send a password reset emaail +- `sending`: A `boolean` to indicate whether the email is being sent +- `error`: Any `Error` returned by Firebase when trying to send the email, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useSendEmailVerification } from 'react-firebase-hooks/auth'; + +const SendEmailVerification = () => { + const [email, setEmail] = useState(''); + const [sendEmailVerification, sending, error] = useSendEmailVerification( + auth + ); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (sending) { + return

Sending...

; + } + return ( +
+ +
+ ); +}; +``` diff --git a/auth/index.ts b/auth/index.ts index 0cdb489..a880dad 100644 --- a/auth/index.ts +++ b/auth/index.ts @@ -1,4 +1,13 @@ export { default as useAuthState, AuthStateHook } from './useAuthState'; +export { default as useCreateUserWithEmailAndPassword } from './useCreateUserWithEmailAndPassword'; +export { + default as useSendEmailVerification, + SendEmailVerificationHook, +} from './useSendEmailVerification'; +export { + default as useSendPasswordResetEmail, + SendPasswordResetEmailHook, +} from './useSendPasswordResetEmail'; export { default as useSignInWithEmailAndPassword } from './useSignInWithEmailAndPassword'; export { useSignInWithApple, @@ -9,6 +18,13 @@ export { useSignInWithTwitter, useSignInWithYahoo, } from './useSignInWithPopup'; -export { default as useCreateUserWithEmailAndPassword } from './useCreateUserWithEmailAndPassword'; +export { + useUpdateEmail, + useUpdatePassword, + useUpdateProfile, + UpdateEmailHook, + UpdatePasswordHook, + UpdateProfileHook, +} from './useUpdateUser'; -export { EmailAndPasswordActionHook } from './types'; +export { EmailAndPasswordActionHook, SignInWithPopupHook } from './types'; diff --git a/auth/useSendEmailVerification.ts b/auth/useSendEmailVerification.ts new file mode 100644 index 0000000..5c91f34 --- /dev/null +++ b/auth/useSendEmailVerification.ts @@ -0,0 +1,39 @@ +import { + Auth, + AuthError, + sendEmailVerification as fbSendEmailVerification, +} from 'firebase/auth'; +import { useMemo, useState } from 'react'; + +export type SendEmailVerificationHook = [ + () => Promise, + boolean, + AuthError | Error | undefined +]; + +export default (auth: Auth): SendEmailVerificationHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const sendEmailVerification = async () => { + setLoading(true); + try { + if (auth.currentUser) { + await fbSendEmailVerification(auth.currentUser); + } else { + setError(new Error('No user is logged in') as AuthError); + } + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }; + + const resArray: SendEmailVerificationHook = [ + sendEmailVerification, + loading, + error, + ]; + return useMemo(() => resArray, resArray); +}; diff --git a/auth/useSendPasswordResetEmail.ts b/auth/useSendPasswordResetEmail.ts new file mode 100644 index 0000000..13c20d3 --- /dev/null +++ b/auth/useSendPasswordResetEmail.ts @@ -0,0 +1,39 @@ +import { + Auth, + AuthError, + sendPasswordResetEmail as fbSendPasswordResetEmail, +} from 'firebase/auth'; +import { useMemo, useState } from 'react'; + +export type SendPasswordResetEmailHook = [ + (email: string) => Promise, + boolean, + AuthError | Error | undefined +]; + +export default (auth: Auth): SendPasswordResetEmailHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const sendPasswordResetEmail = async (email: string) => { + setLoading(true); + try { + if (auth.currentUser) { + await fbSendPasswordResetEmail(auth, email); + } else { + setError(new Error('No user is logged in') as AuthError); + } + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }; + + const resArray: SendPasswordResetEmailHook = [ + sendPasswordResetEmail, + loading, + error, + ]; + return useMemo(() => resArray, resArray); +}; diff --git a/auth/useUpdateUser.ts b/auth/useUpdateUser.ts new file mode 100644 index 0000000..6c26f32 --- /dev/null +++ b/auth/useUpdateUser.ts @@ -0,0 +1,92 @@ +import { + Auth, + AuthError, + updateEmail as fbUpdateEmail, + updatePassword as fbUpdatePassword, + updateProfile as fbUpdateProfile, +} from 'firebase/auth'; +import { useMemo, useState } from 'react'; + +type Profile = { + displayName?: string | null; + photoURL?: string | null; +}; + +export type UpdateUserHook = [M, boolean, AuthError | Error | undefined]; + +export type UpdateEmailHook = UpdateUserHook<(email: string) => Promise>; +export type UpdatePasswordHook = UpdateUserHook< + (password: string) => Promise +>; +export type UpdateProfileHook = UpdateUserHook< + (profile: Profile) => Promise +>; + +export const useUpdateEmail = (auth: Auth): UpdateEmailHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const updateEmail = async (email: string) => { + setLoading(true); + try { + if (auth.currentUser) { + await fbUpdateEmail(auth.currentUser, email); + } else { + setError(new Error('No user is logged in') as AuthError); + } + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }; + + const resArray: UpdateEmailHook = [updateEmail, loading, error]; + return useMemo(() => resArray, resArray); +}; + +export const useUpdatePassword = (auth: Auth): UpdatePasswordHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const updatePassword = async (password: string) => { + setLoading(true); + try { + if (auth.currentUser) { + await fbUpdatePassword(auth.currentUser, password); + } else { + setError(new Error('No user is logged in') as AuthError); + } + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }; + + const resArray: UpdatePasswordHook = [updatePassword, loading, error]; + return useMemo(() => resArray, resArray); +}; + +export const useUpdateProfile = (auth: Auth): UpdateProfileHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const updateProfile = async (profile: Profile) => { + setLoading(true); + try { + if (auth.currentUser) { + await fbUpdateProfile(auth.currentUser, profile); + } else { + setError(new Error('No user is logged in') as AuthError); + } + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }; + + const resArray: UpdateProfileHook = [updateProfile, loading, error]; + return useMemo(() => resArray, resArray); +}; From cca15734626d90caeb4357d8de9e7905656b0ad2 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 4 Jan 2022 18:59:50 +0000 Subject: [PATCH 14/29] Bump package.json version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3319c3b..2a4c1b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "4.0.1", + "version": "5.0.0-alpha.0", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", From d9b445ebb6cf1b7567fef0be7d1384e3d11012c9 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 5 Jan 2022 11:27:18 +0000 Subject: [PATCH 15/29] [functions] Add `useHttpsCallable` hook --- auth/useAuthState.ts | 2 +- firestore/types.ts | 1 - functions/index.ts | 4 + functions/package.json | 6 + functions/useHttpsCallable.ts | 38 ++ package-lock.json | 1035 +++++++++++++++++++++++++++------ package.json | 21 +- rollup.config.js | 11 +- 8 files changed, 910 insertions(+), 208 deletions(-) create mode 100644 functions/index.ts create mode 100644 functions/package.json create mode 100644 functions/useHttpsCallable.ts diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 8de83b7..9146ab0 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -19,7 +19,7 @@ export default (auth: Auth, options?: AuthStateOptions): AuthStateHook => { auth, async (user) => { if (options?.onUserChanged) { - // onUserLoaded function to process custom claims on any other trigger function + // onUserChanged function to process custom claims on any other trigger function try { await options.onUserChanged(user); } catch (e) { diff --git a/firestore/types.ts b/firestore/types.ts index a3cf937..710f0ec 100644 --- a/firestore/types.ts +++ b/firestore/types.ts @@ -1,6 +1,5 @@ import { DocumentData, - DocumentReference, DocumentSnapshot, FirestoreError, QuerySnapshot, diff --git a/functions/index.ts b/functions/index.ts new file mode 100644 index 0000000..6d223cd --- /dev/null +++ b/functions/index.ts @@ -0,0 +1,4 @@ +export { + default as useHttpsCallable, + HttpsCallableHook, +} from './useHttpsCallable'; diff --git a/functions/package.json b/functions/package.json new file mode 100644 index 0000000..1b12bfa --- /dev/null +++ b/functions/package.json @@ -0,0 +1,6 @@ +{ + "name": "react-firebase-hooks/functions", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "typings": "dist/functions/index.d.ts" +} diff --git a/functions/useHttpsCallable.ts b/functions/useHttpsCallable.ts new file mode 100644 index 0000000..582af31 --- /dev/null +++ b/functions/useHttpsCallable.ts @@ -0,0 +1,38 @@ +import { HttpsCallable, HttpsCallableResult } from 'firebase/functions'; +import { useMemo, useState } from 'react'; + +export type HttpsCallableHook = [ + (data?: RequestData) => Promise | unknown>, + boolean, + Error | undefined +]; + +export default ( + callable: HttpsCallable +): HttpsCallableHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const callCallable = async ( + data?: RequestData + ): Promise | undefined> => { + setLoading(true); + try { + return callable(data); + } catch (err) { + setError(err as Error); + } finally { + setLoading(false); + } + }; + + const resArray: HttpsCallableHook = [ + callCallable, + loading, + error, + ]; + return useMemo>( + () => resArray, + resArray + ); +}; diff --git a/package-lock.json b/package-lock.json index 661d80d..c75d9c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,35 @@ { "name": "react-firebase-hooks", - "version": "4.0.1", + "version": "5.0.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, "@firebase/analytics": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.1.tgz", @@ -662,6 +688,32 @@ "yargs": "^16.1.1" } }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -726,13 +778,158 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", "dev": true }, - "@types/acorn": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.5.tgz", - "integrity": "sha512-603sPiZ4GVRHPvn6vNgEAvJewKsy+zwRWYS2MeIMemgoAtcjlw2G3lALxrb9OPA17J28bkB71R33yXlQbUatCA==", + "@rollup/plugin-commonjs": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", + "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", "dev": true, "requires": { - "@types/estree": "*" + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + } + } + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "resolve": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "dev": true, + "requires": { + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "@rollup/plugin-node-resolve": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.2.tgz", + "integrity": "sha512-xyqbuf1vyOPC60jEKhx3DBHunymnCJswzjNTKfX4Jz7zCPar1UqbRZCNY1u5QaXh97beaFTWdoUUWiV4qX8o/g==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "builtin-modules": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "resolve": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "dev": true, + "requires": { + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "@rollup/pluginutils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", + "integrity": "sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==", + "dev": true, + "requires": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + } + } + }, + "@ts-type/package-dts": { + "version": "1.0.56", + "resolved": "https://registry.npmjs.org/@ts-type/package-dts/-/package-dts-1.0.56.tgz", + "integrity": "sha512-nBjbCaVV+R81jk7q/6sRuNYOhDAbBOmaCkPI2quK0Rnye2f4FUHX+4JL+3rDPiRPHxgX3pNfVKKHBlPAHiRddw==", + "dev": true, + "requires": { + "@types/semver": "^7.3.9", + "ts-type": "^2.1.2" } }, "@types/estree": { @@ -741,12 +938,37 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, + "@types/fs-extra": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", + "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", "dev": true }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, "@types/node": { "version": "16.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.1.tgz", @@ -769,19 +991,39 @@ "csstype": "^3.0.2" } }, - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==", "dev": true }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "@yarn-tool/resolve-package": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/@yarn-tool/resolve-package/-/resolve-package-1.0.41.tgz", + "integrity": "sha512-ybJqQztdSkTr9Z0BkPo5OlQBZoHlXHHXxssPJgFT8lFgyOxlhkjUg3WBFn7MSK6JMUoXU32wd2KKFBL/Fgsb8Q==", "dev": true, "requires": { - "acorn": "^5.0.0" + "@ts-type/package-dts": "^1.0.56", + "pkg-dir": "< 6 >= 5", + "tslib": "^2.3.1", + "upath2": "^3.1.12" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } } }, "ansi-regex": { @@ -799,6 +1041,12 @@ "color-convert": "^2.0.1" } }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -815,11 +1063,51 @@ "concat-map": "0.0.1" } }, - "builtin-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz", - "integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==", - "dev": true + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } }, "cliui": { "version": "7.0.4", @@ -847,16 +1135,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "concat-map": { @@ -883,13 +1171,19 @@ "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", "dev": true }, - "date-time": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-2.1.0.tgz", - "integrity": "sha512-/9+C44X7lot0IeiyfgJmETtRMhBidBYM2QFFIkGa0U1k+hSyY87Nw7PY3eDqpvCBm7I3WCSfPeZskW/YYq6m4g==", + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { - "time-zone": "^1.0.0" + "path-type": "^4.0.0" } }, "emoji-regex": { @@ -904,12 +1198,34 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, - "estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -919,6 +1235,84 @@ "websocket-driver": ">=0.5.1" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "firebase": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.1.0.tgz", @@ -954,13 +1348,13 @@ } }, "fs-extra": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", - "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^3.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, @@ -970,6 +1364,19 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -990,12 +1397,52 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, "graceful-fs": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "http-parser-js": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", @@ -1008,6 +1455,12 @@ "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==", "dev": true }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -1030,25 +1483,61 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", "dev": true }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true + }, "is-reference": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.3.tgz", - "integrity": "sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "requires": { - "@types/estree": "0.0.39" + "@types/estree": "*" } }, "isarray": { @@ -1057,6 +1546,27 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1064,9 +1574,9 @@ "dev": true }, "jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { "graceful-fs": "^4.1.6" @@ -1093,11 +1603,14 @@ "immediate": "~3.0.5" } }, - "locate-character": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-2.0.5.tgz", - "integrity": "sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==", - "dev": true + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } }, "lodash.camelcase": { "version": "4.3.0", @@ -1120,13 +1633,35 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "magic-string": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", - "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "vlq": "^0.2.2" + "semver": "^6.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, "minimatch": { @@ -1159,18 +1694,36 @@ "wrappy": "1" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, - "parse-ms": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", - "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", - "dev": true - }, "path": { "version": "0.12.7", "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", @@ -1181,33 +1734,85 @@ "util": "^0.10.3" } }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-is-network-drive": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/path-is-network-drive/-/path-is-network-drive-1.0.13.tgz", + "integrity": "sha512-Hg74mRN6mmXV+gTm3INjFK40ncAmC/Lo4qoQaSZ+GT3hZzlKdWQSqAjqyPeW0SvObP2W073WyYEBWY9d3wOm3A==", + "dev": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "path-strip-sep": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/path-strip-sep/-/path-strip-sep-1.0.10.tgz", + "integrity": "sha512-JpCy+8LAJQQTO1bQsb/84s1g+/Stm3h39aOpPRBQ/paMUGVPPZChLTOTKHoaCkc/6sKuF7yVsnq5Pe1S6xQGcA==", + "dev": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "pretty-ms": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz", - "integrity": "sha512-ZypexbfVUGTFxb0v+m1bUyy92DHe5SyYlnyY0msyms5zd3RwyvNgyxZZsXXgoyzlxjx5MiqtXUdhUfvQbe0A2Q==", + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "dev": true, "requires": { - "parse-ms": "^1.0.0" + "find-up": "^5.0.0" } }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -1247,6 +1852,12 @@ "long": "^4.0.0" } }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "react": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", @@ -1278,21 +1889,12 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -1303,84 +1905,48 @@ } }, "rollup": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.57.1.tgz", - "integrity": "sha512-I18GBqP0qJoJC1K1osYjreqA8VAKovxuI3I81RSk0Dmr4TgloI0tAULjZaox8OsJ+n7XRrhH6i0G2By/pj1LCA==", + "version": "2.63.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz", + "integrity": "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==", "dev": true, "requires": { - "@types/acorn": "^4.0.3", - "acorn": "^5.5.3", - "acorn-dynamic-import": "^3.0.0", - "date-time": "^2.1.0", - "is-reference": "^1.1.0", - "locate-character": "^2.0.5", - "pretty-ms": "^3.1.0", - "require-relative": "^0.8.7", - "rollup-pluginutils": "^2.0.1", - "signal-exit": "^3.0.2", - "sourcemap-codec": "^1.4.1" - } - }, - "rollup-plugin-commonjs": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.0.tgz", - "integrity": "sha512-NrfE0g30QljNCnlJr7I2Xguz+44mh0dCxvfxwLnCwtaCK2LwFUp1zzAs8MQuOfhH4mRskqsjfOwGUap/L+WtEw==", - "dev": true, - "requires": { - "estree-walker": "^0.5.1", - "magic-string": "^0.22.4", - "resolve": "^1.5.0", - "rollup-pluginutils": "^2.0.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", - "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==", - "dev": true - } + "fsevents": "~2.3.2" } }, "rollup-plugin-copy": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-0.2.3.tgz", - "integrity": "sha1-2sGrgdHyILrrmOXEwBCCUuHtu5g=", - "dev": true, - "requires": { - "colors": "^1.1.2", - "fs-extra": "^3.0.0" - } - }, - "rollup-plugin-node-resolve": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz", - "integrity": "sha512-9zHGr3oUJq6G+X0oRMYlzid9fXicBdiydhwGChdyeNRGPcN/majtegApRKHLR5drboUvEWU+QeUmGTyEZQs3WA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", + "integrity": "sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==", "dev": true, "requires": { - "builtin-modules": "^2.0.0", - "is-module": "^1.0.0", - "resolve": "^1.1.6" + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" } }, "rollup-plugin-typescript2": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.12.0.tgz", - "integrity": "sha512-UvHzfJyBWyV+d4Av7FG79PTipIK1VrNQoDrNWszAYd7i/ZcCfBg3rPkPiK9Z+WEHXeWBbCnxJlSqR2zKWgEkiw==", + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.31.1.tgz", + "integrity": "sha512-sklqXuQwQX+stKi4kDfEkneVESPi3YM/2S899vfRdF9Yi40vcC50Oq4A4cSZJNXsAQE/UsBZl5fAOsBLziKmjw==", "dev": true, "requires": { - "fs-extra": "^5.0.0", - "resolve": "^1.5.0", - "rollup-pluginutils": "^2.0.1", - "tslib": "^1.9.0" + "@rollup/pluginutils": "^4.1.0", + "@yarn-tool/resolve-package": "^1.0.36", + "find-cache-dir": "^3.3.1", + "fs-extra": "8.1.0", + "resolve": "1.20.0", + "tslib": "2.2.0" }, "dependencies": { "fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } @@ -1393,25 +1959,44 @@ "requires": { "graceful-fs": "^4.1.6" } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true } } }, "rollup-plugin-uglify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-3.0.0.tgz", - "integrity": "sha512-dehLu9eRRoV4l09aC+ySntRw1OAfoyKdbk8Nelblj03tHoynkSybqyEpgavemi1LBOH6S1vzI58/mpxkZIe1iQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.4.tgz", + "integrity": "sha512-ddgqkH02klveu34TF0JqygPwZnsbhHVI6t8+hGTcYHngPkQb5MIHI0XiztXIN/d6V9j+efwHAqEL7LspSxQXGw==", "dev": true, "requires": { - "uglify-es": "^3.3.7" + "@babel/code-frame": "^7.0.0", + "jest-worker": "^24.0.0", + "serialize-javascript": "^2.1.2", + "uglify-js": "^3.4.9" } }, - "rollup-pluginutils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz", - "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==", + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { - "estree-walker": "^0.6.1" + "queue-microtask": "^1.2.2" } }, "safe-buffer": { @@ -1443,28 +2028,34 @@ } } }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", "dev": true }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "sourcemap-codec": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", - "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, "string-width": { @@ -1496,10 +2087,19 @@ "ansi-regex": "^5.0.1" } }, - "time-zone": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha1-mcW/VZWJZq9tBtg73zgA3IL67F0=", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, "tmp": { @@ -1522,27 +2122,55 @@ } } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-type": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ts-type/-/ts-type-2.1.2.tgz", + "integrity": "sha512-64/2XoaipFXa/bidwXChQCSJ3VaCUVKrEaRPXiP5gLIlkPoAHfnwOMC0EWr42DsTm/+qYYuNwzxc0VQroeDs+g==", + "dev": true, + "requires": { + "tslib": "^2.3.1", + "typedarray-dts": "^1.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "typedarray-dts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typedarray-dts/-/typedarray-dts-1.0.0.tgz", + "integrity": "sha512-Ka0DBegjuV9IPYFT1h0Qqk5U4pccebNIJCGl8C5uU7xtOs+jpJvKGAY4fHGK25hTmXZOEUl9Cnsg5cS6K/b5DA==", "dev": true }, "typescript": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz", - "integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - } + "uglify-js": { + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", + "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", + "dev": true }, "universalify": { "version": "0.1.2", @@ -1550,6 +2178,25 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "upath2": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/upath2/-/upath2-3.1.12.tgz", + "integrity": "sha512-yC3eZeCyCXFWjy7Nu4pgjLhXNYjuzuUmJiRgSSw6TJp8Emc+E4951HGPJf+bldFC5SL7oBLeNbtm1fGzXn2gxw==", + "dev": true, + "requires": { + "path-is-network-drive": "^1.0.13", + "path-strip-sep": "^1.0.10", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -1573,12 +2220,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, "websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -1651,6 +2292,12 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 2a4c1b4..5714897 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,11 @@ "firestore/dist/util", "firestore/dist/*.js.flow", "firestore/package.json", + "functions/dist/*.js", + "functions/dist/functions", + "functions/dist/util", + "functions/dist/*.js.flow", + "functions/package.json", "storage/dist/*.js", "storage/dist/storage", "storage/dist/util", @@ -49,21 +54,21 @@ }, "main": "dist/index.cjs.js", "module": "dist/index.esm.js", - "dependencies": {}, "devDependencies": { + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-node-resolve": "^13.1.2", "@types/react": "^17.0.0", "firebase": "9.1.0", "path": "^0.12.7", "prettier": "2.2.1", "react": "^17.0.1", "rimraf": "^2.6.2", - "rollup": "0.57.1", - "rollup-plugin-commonjs": "9.1.0", - "rollup-plugin-copy": "^0.2.3", - "rollup-plugin-node-resolve": "3.3.0", - "rollup-plugin-typescript2": "0.12.0", - "rollup-plugin-uglify": "3.0.0", - "typescript": "2.8.1" + "rollup": "^2.63.0", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-typescript2": "^0.31.1", + "rollup-plugin-uglify": "^6.0.4", + "tslib": "^2.3.1", + "typescript": "^4.5.4" }, "peerDependencies": { "react": ">= 16.8.0", diff --git a/rollup.config.js b/rollup.config.js index 7f8fc3d..3ca8ea9 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,20 +1,22 @@ import { resolve } from 'path'; -import commonjs from 'rollup-plugin-commonjs'; +import commonjs from '@rollup/plugin-commonjs'; import copy from 'rollup-plugin-copy'; -import resolveModule from 'rollup-plugin-node-resolve'; +import resolveModule from '@rollup/plugin-node-resolve'; import typescript from 'rollup-plugin-typescript2'; -import uglify from 'rollup-plugin-uglify'; +import { uglify } from 'rollup-plugin-uglify'; import pkg from './package.json'; import authPkg from './auth/package.json'; import databasePkg from './database/package.json'; import firestorePkg from './firestore/package.json'; +import functionsPkg from './functions/package.json'; import storagePkg from './storage/package.json'; const pkgsByName = { auth: authPkg, database: databasePkg, firestore: firestorePkg, + functions: functionsPkg, storage: storagePkg, }; @@ -32,10 +34,11 @@ const external = [ 'firebase/auth', 'firebase/database', 'firebase/firestore', + 'firebase/functions', 'firebase/storage', ]; -const components = ['auth', 'database', 'firestore', 'storage']; +const components = ['auth', 'database', 'firestore', 'functions', 'storage']; export default components .map((component) => { From 455a8d9c6fe8183f3e6f798ec54d3fc0b5276033 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 5 Jan 2022 11:31:12 +0000 Subject: [PATCH 16/29] [functions] Tweak `useHttpsCallable` function signature --- functions/useHttpsCallable.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/functions/useHttpsCallable.ts b/functions/useHttpsCallable.ts index 582af31..57916f3 100644 --- a/functions/useHttpsCallable.ts +++ b/functions/useHttpsCallable.ts @@ -1,4 +1,8 @@ -import { HttpsCallable, HttpsCallableResult } from 'firebase/functions'; +import { + Functions, + httpsCallable, + HttpsCallableResult, +} from 'firebase/functions'; import { useMemo, useState } from 'react'; export type HttpsCallableHook = [ @@ -8,7 +12,8 @@ export type HttpsCallableHook = [ ]; export default ( - callable: HttpsCallable + functions: Functions, + name: string ): HttpsCallableHook => { const [error, setError] = useState(); const [loading, setLoading] = useState(false); @@ -16,6 +21,7 @@ export default ( const callCallable = async ( data?: RequestData ): Promise | undefined> => { + const callable = httpsCallable(functions, name); setLoading(true); try { return callable(data); From f2d4fc533165594e934009f06aac2e7fa1b65299 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 5 Jan 2022 11:42:12 +0000 Subject: [PATCH 17/29] [functions] Add README.md --- functions/README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 functions/README.md diff --git a/functions/README.md b/functions/README.md new file mode 100644 index 0000000..640dd28 --- /dev/null +++ b/functions/README.md @@ -0,0 +1,65 @@ +# React Firebase Hooks - Cloud Functions + +React Firebase Hooks provides a convenience hook for HttpsCallable functions, providing an `error` and `loading` property +to give a complete lifecycle for executing a HttpsCallable function on Firebase Cloud Functions. + +All hooks can be imported from `react-firebase-hooks/functions`, e.g. + +```js +import { useHttpsCallable } from 'react-firebase-hooks/functions'; +``` + +List of Cloud Firestore hooks: + +- [React Firebase Hooks - Cloud Firestore](#react-firebase-hooks---cloud-firestore) + - [useHttpsCallable](#usehttpscallable) + - [Full example](#full-example) + +### useHttpsCallable + +```js +const [executeCallable, loading, error] = useHttpsCallable(functions, name); +``` + +Generate a callable function and monitor its execution. + +The `useHttpsCallable` hook takes the following parameters: + +- `functions`: `functions.Functions` instance for your Firebase app +- `name`: A `string` representing the name of the function to call + +Returns: + +- `executeCallable(data)`: a function you can call to execute the HttpsCallable +- `loading`: a `boolean` to indicate if the function is still being executed +- `error`: Any `Error` returned by Firebase when trying to execute the function, or `undefined` if there is no error + +#### Full example + +```js +import { getFunctions } from 'firebase/functions'; +import { useHttpsCallable } from 'react-firebase-hooks/functions'; + +const HttpsCallable = () => { + const [executeCallable, executing, error] = useHttpsCallable( + getFunctions(firebaseApp), + 'myHttpsCallable' + ); + return ( +
+

+ {error && Error: {JSON.stringify(error)}} + {executing && Function executing...} + +

+
+ ); +}; +``` From df5ecdfc546368662d975767f9efe6383c98c04e Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 7 Jan 2022 16:23:27 +0000 Subject: [PATCH 18/29] [messaging] Add `useToken` hook --- auth/useAuthState.ts | 2 +- functions/README.md | 4 ++-- messaging/README.md | 54 ++++++++++++++++++++++++++++++++++++++++++ messaging/index.ts | 1 + messaging/package.json | 6 +++++ messaging/useToken.ts | 19 +++++++++++++++ package.json | 5 ++++ rollup.config.js | 12 +++++++++- 8 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 messaging/README.md create mode 100644 messaging/index.ts create mode 100644 messaging/package.json create mode 100644 messaging/useToken.ts diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 9146ab0..3c80778 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -34,7 +34,7 @@ export default (auth: Auth, options?: AuthStateOptions): AuthStateHook => { return () => { listener(); }; - }, [auth]); + }, [auth, options]); const resArray: AuthStateHook = [value, loading, error]; return useMemo(() => resArray, resArray); diff --git a/functions/README.md b/functions/README.md index 640dd28..7cabbc5 100644 --- a/functions/README.md +++ b/functions/README.md @@ -9,9 +9,9 @@ All hooks can be imported from `react-firebase-hooks/functions`, e.g. import { useHttpsCallable } from 'react-firebase-hooks/functions'; ``` -List of Cloud Firestore hooks: +List of Cloud Functions hooks: -- [React Firebase Hooks - Cloud Firestore](#react-firebase-hooks---cloud-firestore) +- [React Firebase Hooks - Cloud Functions](#react-firebase-hooks---cloud-functions) - [useHttpsCallable](#usehttpscallable) - [Full example](#full-example) diff --git a/messaging/README.md b/messaging/README.md new file mode 100644 index 0000000..2e23f98 --- /dev/null +++ b/messaging/README.md @@ -0,0 +1,54 @@ +# React Firebase Hooks - Cloud Messaging + +React Firebase Hooks provides a convenience hook for getting a cloud messaging token, providing an `error` and `loading` property +to give a complete lifecycle for accessing the cloud messaging token on Firebase Cloud Messaging. + +All hooks can be imported from `react-firebase-hooks/messaging`, e.g. + +```js +import { useToken } from 'react-firebase-hooks/messaging'; +``` + +List of Cloud Messaging hooks: + +- [React Firebase Hooks - Cloud Messaging](#react-firebase-hooks---cloud-messaging) + - [useToken](#usetoken) + - [Full example](#full-example) + +### useToken + +```js +const [token, loading, error] = useToken(messaging); +``` + +Get a token from Firebase Cloud Messaging + +The `useToken` hook takes the following parameters: + +- `messaging`: `messaging.Messaging` instance for your Firebase app + +Returns: + +- `token`: a `string` token to use with cloud messaging +- `loading`: a `boolean` to indicate if the function is still being executed +- `error`: Any `Error` returned by Firebase when trying to execute the function, or `undefined` if there is no error + +#### Full example + +```js +import { getMessaging } from 'firebase/messaging'; +import { useToken } from 'react-firebase-hooks/messaging'; + +const MessagingToken = () => { + const [token, loading, error] = useToken(getMessaging(firebaseApp)); + return ( +
+

+ {error && Error: {JSON.stringify(error)}} + {loading && Loading token...} + {token && Token:{token}} +

+
+ ); +}; +``` diff --git a/messaging/index.ts b/messaging/index.ts new file mode 100644 index 0000000..63bb228 --- /dev/null +++ b/messaging/index.ts @@ -0,0 +1 @@ +export { default as useToken, TokenHook } from './useToken'; diff --git a/messaging/package.json b/messaging/package.json new file mode 100644 index 0000000..20d3e61 --- /dev/null +++ b/messaging/package.json @@ -0,0 +1,6 @@ +{ + "name": "react-firebase-hooks/messaging", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "typings": "dist/functions/index.d.ts" +} diff --git a/messaging/useToken.ts b/messaging/useToken.ts new file mode 100644 index 0000000..1817c26 --- /dev/null +++ b/messaging/useToken.ts @@ -0,0 +1,19 @@ +import { Messaging, getToken } from 'firebase/messaging'; +import { useEffect, useMemo } from 'react'; +import { LoadingHook, useLoadingValue } from '../util'; + +export type TokenHook = LoadingHook; + +export default (messaging: Messaging): TokenHook => { + const { error, loading, setError, setValue, value } = useLoadingValue< + string | null, + Error + >(); + + useEffect(() => { + getToken(messaging).then(setValue).catch(setError); + }, [messaging]); + + const resArray: TokenHook = [value, loading, error]; + return useMemo(() => resArray, resArray); +}; diff --git a/package.json b/package.json index 5714897..b001008 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,11 @@ "functions/dist/util", "functions/dist/*.js.flow", "functions/package.json", + "messaging/dist/*.js", + "messaging/dist/functions", + "messaging/dist/util", + "messaging/dist/*.js.flow", + "messaging/package.json", "storage/dist/*.js", "storage/dist/storage", "storage/dist/util", diff --git a/rollup.config.js b/rollup.config.js index 3ca8ea9..902d986 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -10,6 +10,7 @@ import authPkg from './auth/package.json'; import databasePkg from './database/package.json'; import firestorePkg from './firestore/package.json'; import functionsPkg from './functions/package.json'; +import messagingPkg from './messaging/package.json'; import storagePkg from './storage/package.json'; const pkgsByName = { @@ -17,6 +18,7 @@ const pkgsByName = { database: databasePkg, firestore: firestorePkg, functions: functionsPkg, + messaging: messagingPkg, storage: storagePkg, }; @@ -35,10 +37,18 @@ const external = [ 'firebase/database', 'firebase/firestore', 'firebase/functions', + 'firebase/messaging', 'firebase/storage', ]; -const components = ['auth', 'database', 'firestore', 'functions', 'storage']; +const components = [ + 'auth', + 'database', + 'firestore', + 'functions', + 'messaging', + 'storage', +]; export default components .map((component) => { From d688bee60700e840585ad357900c1e556b72005e Mon Sep 17 00:00:00 2001 From: Martin Kubie Date: Fri, 7 Jan 2022 20:48:17 -0800 Subject: [PATCH 19/29] Adds active request protection to *Once queries --- firestore/useCollection.ts | 23 ++++++++++++++++++++--- firestore/useDocument.ts | 22 +++++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index eafa2db..1b5427a 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -66,7 +66,7 @@ const useCollectionInternal = ( return; } if (listen) { - const listener = options?.snapshotListenOptions + const unsubscribe = options?.snapshotListenOptions ? onSnapshot( ref.current, options.snapshotListenOptions, @@ -76,11 +76,28 @@ const useCollectionInternal = ( : onSnapshot(ref.current, setValue, setError); return () => { - listener(); + unsubscribe(); }; } else { + let effectActive = true; + const get = getDocsFnFromGetOptions(options?.getOptions); - get(ref.current).then(setValue).catch(setError); + + get(ref.current) + .then((result) => { + if (effectActive) { + setValue(result); + } + }) + .catch((error) => { + if (effectActive) { + setError(error); + } + }); + + return () => { + effectActive = false; + }; } }, [ref.current]); diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index a4b286f..22946bc 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -65,7 +65,7 @@ const useDocumentInternal = ( return; } if (listen) { - const listener = options?.snapshotListenOptions + const unsubscribe = options?.snapshotListenOptions ? onSnapshot( ref.current, options.snapshotListenOptions, @@ -75,12 +75,28 @@ const useDocumentInternal = ( : onSnapshot(ref.current, setValue, setError); return () => { - listener(); + unsubscribe(); }; } else { + let effectActive = true; + const get = getDocFnFromGetOptions(options?.getOptions); - get(ref.current).then(setValue).catch(setError); + get(ref.current) + .then((doc) => { + if (effectActive) { + setValue(doc); + } + }) + .catch((error) => { + if (effectActive) { + setError(error); + } + }); + + return () => { + effectActive = false; + }; } }, [ref.current]); From 416de01b8cbbb1d27bd8b726d5e73204f44bd296 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 11 Jan 2022 17:23:02 +0000 Subject: [PATCH 20/29] [firestore] Add snapshot to `*Data` responses to give more control to the developer --- firestore/README.md | 16 ++++++++++++---- firestore/types.ts | 17 ++++++++++++----- firestore/useCollection.ts | 2 +- firestore/useDocument.ts | 2 +- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/firestore/README.md b/firestore/README.md index be356bd..4c00918 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -115,7 +115,8 @@ Returns: ### useCollectionData ```js -const [values, loading, error] = useCollectionData < T > (query, options); +const [values, loading, error, snapshot] = + useCollectionData < T > (query, options); ``` As `useCollection`, but this hook extracts a typed list of the `firestore.QuerySnapshot.docs` values, rather than the @@ -133,11 +134,13 @@ Returns: - `values`: an array of `T`, or `undefined` if no query is supplied - `loading`: a `boolean` to indicate if the data is still being loaded - `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error +- `snapshot`: a `firestore.QuerySnapshot`, or `undefined` if no query is supplied. This allows access to the underlying snapshot if needed for any reason, e.g. to view the snapshot metadata ### useCollectionDataOnce ```js -const [values, loading, error] = useCollectionDataOnce < T > (query, options); +const [values, loading, error, snapshot] = + useCollectionDataOnce < T > (query, options); ``` As `useCollectionData`, but this hook will only read the current value of the `firestore.Query`. @@ -155,6 +158,7 @@ Returns: - `values`: an array of `T`, or `undefined` if no query is supplied - `loading`: a `boolean` to indicate if the data is still being loaded - `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error +- `snapshot`: a `firestore.QuerySnapshot`, or `undefined` if no query is supplied. This allows access to the underlying snapshot if needed for any reason, e.g. to view the snapshot metadata ### useDocument @@ -225,7 +229,8 @@ Returns: ### useDocumentData ```js -const [value, loading, error] = useDocumentData < T > (reference, options); +const [value, loading, error, snapshot] = + useDocumentData < T > (reference, options); ``` As `useDocument`, but this hook extracts the typed contents of `firestore.DocumentSnapshot.data()`, rather than the @@ -243,11 +248,13 @@ Returns: - `value`: `T`, or `undefined` if no query is supplied - `loading`: a `boolean` to indicate if the data is still being loaded - `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error +- `snapshot`: a `firestore.DocumentSnapshot`, or `undefined` if no query is supplied. This allows access to the underlying snapshot if needed for any reason, e.g. to view the snapshot metadata ### useDocumentDataOnce ```js -const [value, loading, error] = useDocumentDataOnce < T > (reference, options); +const [value, loading, error, snapshot] = + useDocumentDataOnce < T > (reference, options); ``` As `useDocument`, but this hook will only read the current value of the `firestore.DocumentReference`. @@ -265,6 +272,7 @@ Returns: - `value`: `T`, or `undefined` if no query is supplied - `loading`: a `boolean` to indicate if the data is still being loaded - `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error +- `snapshot`: a `firestore.DocumentSnapshot`, or `undefined` if no query is supplied. This allows access to the underlying snapshot if needed for any reason, e.g. to view the snapshot metadata ## Transforming data diff --git a/firestore/types.ts b/firestore/types.ts index 710f0ec..ab97ade 100644 --- a/firestore/types.ts +++ b/firestore/types.ts @@ -27,13 +27,20 @@ export type CollectionHook = LoadingHook< QuerySnapshot, FirestoreError >; -export type CollectionDataHook = LoadingHook< - T[], - FirestoreError ->; +export type CollectionDataHook = [ + T[] | undefined, + boolean, + FirestoreError | undefined, + QuerySnapshot | undefined +]; export type DocumentHook = LoadingHook< DocumentSnapshot, FirestoreError >; -export type DocumentDataHook = LoadingHook; +export type DocumentDataHook = [ + T | undefined, + boolean, + FirestoreError | undefined, + DocumentSnapshot | undefined +]; diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index 1b5427a..cddbee7 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -125,7 +125,7 @@ const useCollectionDataInternal = ( [snapshots, snapshotOptions] ); - const resArray: CollectionDataHook = [values, loading, error]; + const resArray: CollectionDataHook = [values, loading, error, snapshots]; return useMemo(() => resArray, resArray); }; diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index 22946bc..feb819b 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -124,7 +124,7 @@ const useDocumentDataInternal = ( snapshotOptions, ]); - const resArray: DocumentDataHook = [value, loading, error]; + const resArray: DocumentDataHook = [value, loading, error, snapshot]; return useMemo(() => resArray, resArray); }; From 0c7036875214c772280e1663f6aa4a35a31c4625 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 12 Jan 2022 13:29:07 +0000 Subject: [PATCH 21/29] [firestore] Add manual reload functionality to `*Once` hooks --- firestore/README.md | 6 +- firestore/types.ts | 24 ++++-- firestore/useCollection.ts | 161 +++++++++++++++++++++---------------- firestore/useDocument.ts | 154 ++++++++++++++++++++--------------- package-lock.json | 3 +- package.json | 2 +- 6 files changed, 207 insertions(+), 143 deletions(-) diff --git a/firestore/README.md b/firestore/README.md index 4c00918..ff7042d 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -208,7 +208,7 @@ const FirestoreDocument = () => { ### useDocumentOnce ```js -const [snapshot, loading, error] = useDocumentOnce(reference, options); +const [snapshot, loading, error, reload] = useDocumentOnce(reference, options); ``` Retrieve the current value of the `firestore.DocumentReference`. @@ -225,6 +225,7 @@ Returns: - `snapshot`: a `firestore.DocumentSnapshot`, or `undefined` if no reference is supplied - `loading`: a `boolean` to indicate if the data is still being loaded - `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error +- `reload()`: a function that can be called to trigger a reload of the data ### useDocumentData @@ -253,7 +254,7 @@ Returns: ### useDocumentDataOnce ```js -const [value, loading, error, snapshot] = +const [value, loading, error, snapshot, reload] = useDocumentDataOnce < T > (reference, options); ``` @@ -273,6 +274,7 @@ Returns: - `loading`: a `boolean` to indicate if the data is still being loaded - `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error - `snapshot`: a `firestore.DocumentSnapshot`, or `undefined` if no query is supplied. This allows access to the underlying snapshot if needed for any reason, e.g. to view the snapshot metadata +- `reload()`: a function that can be called to trigger a reload of the data ## Transforming data diff --git a/firestore/types.ts b/firestore/types.ts index ab97ade..3fbb911 100644 --- a/firestore/types.ts +++ b/firestore/types.ts @@ -27,20 +27,32 @@ export type CollectionHook = LoadingHook< QuerySnapshot, FirestoreError >; +export type CollectionOnceHook = [ + ...CollectionHook, + () => Promise +]; export type CollectionDataHook = [ - T[] | undefined, - boolean, - FirestoreError | undefined, + ...LoadingHook, QuerySnapshot | undefined ]; +export type CollectionDataOnceHook = [ + ...CollectionDataHook, + () => Promise +]; export type DocumentHook = LoadingHook< DocumentSnapshot, FirestoreError >; +export type DocumentOnceHook = [ + ...DocumentHook, + () => Promise +]; export type DocumentDataHook = [ - T | undefined, - boolean, - FirestoreError | undefined, + ...LoadingHook, DocumentSnapshot | undefined ]; +export type DocumentDataOnceHook = [ + ...DocumentDataHook, + () => Promise +]; diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index cddbee7..d483a35 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -7,13 +7,16 @@ import { onSnapshot, Query, QuerySnapshot, + SnapshotOptions, } from 'firebase/firestore'; import { useEffect, useMemo } from 'react'; import { useLoadingValue } from '../util'; import { useIsFirestoreQueryEqual } from './helpers'; import { CollectionDataHook, + CollectionDataOnceHook, CollectionHook, + CollectionOnceHook, DataOptions, GetOptions, OnceDataOptions, @@ -25,110 +28,130 @@ export const useCollection = ( query?: Query | null, options?: Options ): CollectionHook => { - return useCollectionInternal(true, query, options); -}; + const { error, loading, reset, setError, setValue, value } = useLoadingValue< + QuerySnapshot, + FirestoreError + >(); + const ref = useIsFirestoreQueryEqual>(query, reset); -export const useCollectionOnce = ( - query?: Query | null, - options?: OnceOptions -): CollectionHook => { - return useCollectionInternal(false, query, options); -}; + useEffect(() => { + if (!ref.current) { + setValue(undefined); + return; + } + const unsubscribe = options?.snapshotListenOptions + ? onSnapshot( + ref.current, + options.snapshotListenOptions, + setValue, + setError + ) + : onSnapshot(ref.current, setValue, setError); -export const useCollectionData = ( - query?: Query | null, - options?: DataOptions -): CollectionDataHook => { - return useCollectionDataInternal(true, query, options); -}; + return () => { + unsubscribe(); + }; + }, [ref.current, options]); -export const useCollectionDataOnce = ( - query?: Query | null, - options?: OnceDataOptions -): CollectionDataHook => { - return useCollectionDataInternal(false, query, options); + const resArray: CollectionHook = [ + value as QuerySnapshot, + loading, + error, + ]; + return useMemo(() => resArray, resArray); }; -const useCollectionInternal = ( - listen: boolean, +export const useCollectionOnce = ( query?: Query | null, - options?: Options & OnceOptions -): CollectionHook => { + options?: OnceOptions +): CollectionOnceHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< QuerySnapshot, FirestoreError >(); + let effectActive = true; const ref = useIsFirestoreQueryEqual>(query, reset); - useEffect(() => { - if (!ref.current) { + const loadData = async ( + query?: Query | null, + options?: Options & OnceOptions + ) => { + if (!query) { setValue(undefined); return; } - if (listen) { - const unsubscribe = options?.snapshotListenOptions - ? onSnapshot( - ref.current, - options.snapshotListenOptions, - setValue, - setError - ) - : onSnapshot(ref.current, setValue, setError); + const get = getDocsFnFromGetOptions(options?.getOptions); - return () => { - unsubscribe(); - }; - } else { - let effectActive = true; - - const get = getDocsFnFromGetOptions(options?.getOptions); + try { + const result = await get(query); + if (effectActive) { + setValue(result); + } + } catch (error) { + if (effectActive) { + setError(error as FirestoreError); + } + } + }; - get(ref.current) - .then((result) => { - if (effectActive) { - setValue(result); - } - }) - .catch((error) => { - if (effectActive) { - setError(error); - } - }); + useEffect(() => { + loadData(ref.current, options); - return () => { - effectActive = false; - }; - } - }, [ref.current]); + return () => { + effectActive = false; + }; + }, [ref.current, options]); - const resArray: CollectionHook = [ + const resArray: CollectionOnceHook = [ value as QuerySnapshot, loading, error, + () => loadData(ref.current, options), ]; return useMemo(() => resArray, resArray); }; -const useCollectionDataInternal = ( - listen: boolean, +export const useCollectionData = ( query?: Query | null, - options?: DataOptions & OnceDataOptions + options?: DataOptions ): CollectionDataHook => { const snapshotOptions = options?.snapshotOptions; - const [snapshots, loading, error] = useCollectionInternal( - listen, + const [snapshots, loading, error] = useCollection(query, options); + const values = getValuesFromSnapshots(snapshots, snapshotOptions); + const resArray: CollectionDataHook = [values, loading, error, snapshots]; + return useMemo(() => resArray, resArray); +}; + +export const useCollectionDataOnce = ( + query?: Query | null, + options?: OnceDataOptions +): CollectionDataOnceHook => { + const snapshotOptions = options?.snapshotOptions; + const [snapshots, loading, error, loadData] = useCollectionOnce( query, options ); - const values = useMemo( - () => snapshots?.docs.map((doc) => doc.data(snapshotOptions)) as T[], - [snapshots, snapshotOptions] - ); - - const resArray: CollectionDataHook = [values, loading, error, snapshots]; + const values = getValuesFromSnapshots(snapshots, snapshotOptions); + const resArray: CollectionDataOnceHook = [ + values, + loading, + error, + snapshots, + loadData, + ]; return useMemo(() => resArray, resArray); }; +const getValuesFromSnapshots = ( + snapshots?: QuerySnapshot, + options?: SnapshotOptions +) => { + return useMemo(() => snapshots?.docs.map((doc) => doc.data(options)) as T[], [ + snapshots, + options, + ]); +}; + const getDocsFnFromGetOptions = ( { source }: GetOptions = { source: 'default' } ) => { diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index feb819b..e4e34db 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -14,108 +14,128 @@ import { useIsFirestoreRefEqual } from './helpers'; import { DataOptions, DocumentDataHook, + DocumentDataOnceHook, DocumentHook, + DocumentOnceHook, GetOptions, OnceDataOptions, OnceOptions, Options, } from './types'; + export const useDocument = ( docRef?: DocumentReference | null, options?: Options ): DocumentHook => { - return useDocumentInternal(true, docRef, options); -}; + const { error, loading, reset, setError, setValue, value } = useLoadingValue< + DocumentSnapshot, + FirestoreError + >(); + const ref = useIsFirestoreRefEqual>(docRef, reset); -export const useDocumentOnce = ( - docRef?: DocumentReference | null, - options?: OnceOptions -): DocumentHook => { - return useDocumentInternal(false, docRef, options); -}; + useEffect(() => { + if (!ref.current) { + setValue(undefined); + return; + } + const unsubscribe = options?.snapshotListenOptions + ? onSnapshot( + ref.current, + options.snapshotListenOptions, + setValue, + setError + ) + : onSnapshot(ref.current, setValue, setError); -export const useDocumentData = ( - docRef?: DocumentReference | null, - options?: DataOptions -): DocumentDataHook => { - return useDocumentDataInternal(true, docRef, options); -}; + return () => { + unsubscribe(); + }; + }, [ref.current, options]); -export const useDocumentDataOnce = ( - docRef?: DocumentReference | null, - options?: OnceDataOptions -): DocumentDataHook => { - return useDocumentDataInternal(false, docRef, options); + const resArray: DocumentHook = [ + value as DocumentSnapshot, + loading, + error, + ]; + return useMemo(() => resArray, resArray); }; -const useDocumentInternal = ( - listen: boolean, +export const useDocumentOnce = ( docRef?: DocumentReference | null, - options?: Options & OnceOptions -): DocumentHook => { + options?: OnceOptions +): DocumentOnceHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< DocumentSnapshot, FirestoreError >(); + let effectActive = true; const ref = useIsFirestoreRefEqual>(docRef, reset); + const loadData = async ( + reference?: DocumentReference | null, + options?: OnceOptions + ) => { + if (!reference) { + setValue(undefined); + return; + } + const get = getDocFnFromGetOptions(options?.getOptions); + + try { + const result = await get(reference); + if (effectActive) { + setValue(result); + } + } catch (error) { + if (effectActive) { + setError(error as FirestoreError); + } + } + }; + useEffect(() => { if (!ref.current) { setValue(undefined); return; } - if (listen) { - const unsubscribe = options?.snapshotListenOptions - ? onSnapshot( - ref.current, - options.snapshotListenOptions, - setValue, - setError - ) - : onSnapshot(ref.current, setValue, setError); - - return () => { - unsubscribe(); - }; - } else { - let effectActive = true; - - const get = getDocFnFromGetOptions(options?.getOptions); - - get(ref.current) - .then((doc) => { - if (effectActive) { - setValue(doc); - } - }) - .catch((error) => { - if (effectActive) { - setError(error); - } - }); - - return () => { - effectActive = false; - }; - } - }, [ref.current]); - const resArray: DocumentHook = [ + loadData(ref.current, options); + + return () => { + effectActive = false; + }; + }, [ref.current, options]); + + const resArray: DocumentOnceHook = [ value as DocumentSnapshot, loading, error, + () => loadData(ref.current, options), ]; return useMemo(() => resArray, resArray); }; -const useDocumentDataInternal = ( - listen: boolean, +export const useDocumentData = ( docRef?: DocumentReference | null, options?: DataOptions ): DocumentDataHook => { const snapshotOptions = options?.snapshotOptions; - const [snapshot, loading, error] = useDocumentInternal( - listen, + const [snapshot, loading, error] = useDocument(docRef, options); + const value = useMemo(() => snapshot?.data(snapshotOptions) as T, [ + snapshot, + snapshotOptions, + ]); + + const resArray: DocumentDataHook = [value, loading, error, snapshot]; + return useMemo(() => resArray, resArray); +}; + +export const useDocumentDataOnce = ( + docRef?: DocumentReference | null, + options?: OnceDataOptions +): DocumentDataOnceHook => { + const snapshotOptions = options?.snapshotOptions; + const [snapshot, loading, error, loadData] = useDocumentOnce( docRef, options ); @@ -124,7 +144,13 @@ const useDocumentDataInternal = ( snapshotOptions, ]); - const resArray: DocumentDataHook = [value, loading, error, snapshot]; + const resArray: DocumentDataOnceHook = [ + value, + loading, + error, + snapshot, + loadData, + ]; return useMemo(() => resArray, resArray); }; diff --git a/package-lock.json b/package-lock.json index c75d9c7..0a199b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2152,7 +2152,8 @@ "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true }, "typedarray-dts": { "version": "1.0.0", diff --git a/package.json b/package.json index b001008..0ef26f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "5.0.0-alpha.0", + "version": "5.0.0-alpha.1", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", From 4bf7d07e95397ad4174177563011e2d071a5888e Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 13:23:36 +0000 Subject: [PATCH 22/29] Export util package and a few misc fixes --- README.md | 13 +++++++------ auth/README.md | 2 +- messaging/package.json | 2 +- package.json | 9 ++++++--- rollup.config.js | 5 ++++- util/package.json | 3 ++- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 183e66b..bd3a71f 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ A set of reusable [React Hooks](https://reactjs.org/docs/hooks-intro.html) for [ [![npm version](https://img.shields.io/npm/v/react-firebase-hooks.svg?style=flat-square)](https://www.npmjs.com/package/react-firebase-hooks) [![npm downloads](https://img.shields.io/npm/dm/react-firebase-hooks.svg?style=flat-square)](https://www.npmjs.com/package/react-firebase-hooks) -This documentation is for v4 of React Firebase Hooks which makes the package compatible with Firebase v9 and drops support for previous versions of Firebase - more details [here](https://github.com/CSFrequency/react-firebase-hooks/releases/tag/v4.0.0). +This documentation is for v5 of React Firebase Hooks which requires Firebase v9 or higher. +- For v3 documentation (Firebase v9), see [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v4.0.2). - For v3 documentation (Firebase v8), see [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v3.0.4). - For v2 documentation, see [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v2.2.0). @@ -28,18 +29,18 @@ This assumes that you’re using the [npm](https://npmjs.com) or [yarn](https:// ## Why? -There has been a **lot** of hype around React Hooks, but this hype merely reflects that there are obvious real world benefits of Hooks to React developers everywhere. - This library explores how React Hooks can work to make integration with Firebase even more straightforward than it already is. It takes inspiration for naming from RxFire and is based on an internal library that we had been using in a number of apps prior to the release of React Hooks. The implementation with hooks is 10x simpler than our previous implementation. -## Upgrading from v3 to v4 +## Upgrading from v4 to v5 -To upgrade your project from v3 to v4 check out the [Release Notes](https://github.com/CSFrequency/react-firebase-hooks/releases/tag/v4.0.0) which have full details of everything that needs to be changed. +To upgrade your project from v4 to v5 check out the [Release Notes](https://github.com/CSFrequency/react-firebase-hooks/releases/tag/v5.0.0) which have full details of everything that needs to be changed. ## Documentation -- [Auth Hooks](/auth) +- [Authentication Hooks](/auth) - [Cloud Firestore Hooks](/firestore) +- [Cloud Functions Hooks](/functions) +- [Cloud Messaging Hooks](/messaging) - [Cloud Storage Hooks](/storage) - [Realtime Database Hooks](/database) diff --git a/auth/README.md b/auth/README.md index 66f7451..e947caf 100644 --- a/auth/README.md +++ b/auth/README.md @@ -23,7 +23,7 @@ List of Auth hooks: - [useUpdateEmail](#useupdateemail) - [useUpdatePassword](#useupdatepassword) - [useUpdateProfile](#useupdateprofile) -- [useSendPasswordResetEmaili](#usesendpasswordresetemail) +- [useSendPasswordResetEmail](#usesendpasswordresetemail) - [useSendEmailVerification](#usesendemailverification) ### useAuthState diff --git a/messaging/package.json b/messaging/package.json index 20d3e61..6dc83a6 100644 --- a/messaging/package.json +++ b/messaging/package.json @@ -2,5 +2,5 @@ "name": "react-firebase-hooks/messaging", "main": "dist/index.cjs.js", "module": "dist/index.esm.js", - "typings": "dist/functions/index.d.ts" + "typings": "dist/messaging/index.d.ts" } diff --git a/package.json b/package.json index 0ef26f6..5246a39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "5.0.0-alpha.1", + "version": "5.0.0-alpha.3", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", @@ -35,7 +35,7 @@ "functions/dist/*.js.flow", "functions/package.json", "messaging/dist/*.js", - "messaging/dist/functions", + "messaging/dist/messaging", "messaging/dist/util", "messaging/dist/*.js.flow", "messaging/package.json", @@ -43,7 +43,10 @@ "storage/dist/storage", "storage/dist/util", "storage/dist/*.js.flow", - "storage/package.json" + "storage/package.json", + "util/dist/*.js", + "util/dist/util", + "util/package.json" ], "repository": { "type": "git", diff --git a/rollup.config.js b/rollup.config.js index 902d986..17383c5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -12,6 +12,7 @@ import firestorePkg from './firestore/package.json'; import functionsPkg from './functions/package.json'; import messagingPkg from './messaging/package.json'; import storagePkg from './storage/package.json'; +import utilPkg from './util/package.json'; const pkgsByName = { auth: authPkg, @@ -20,6 +21,7 @@ const pkgsByName = { functions: functionsPkg, messaging: messagingPkg, storage: storagePkg, + util: utilPkg, }; const plugins = [ @@ -30,7 +32,6 @@ const plugins = [ commonjs(), ]; -const peerDependencies = pkg.peerDependencies || {}; const external = [ ...Object.keys(pkg.peerDependencies), 'firebase/auth', @@ -39,6 +40,7 @@ const external = [ 'firebase/functions', 'firebase/messaging', 'firebase/storage', + 'firebase/util', ]; const components = [ @@ -48,6 +50,7 @@ const components = [ 'functions', 'messaging', 'storage', + 'util', ]; export default components diff --git a/util/package.json b/util/package.json index 0911fa5..0590645 100644 --- a/util/package.json +++ b/util/package.json @@ -1,5 +1,6 @@ { "name": "react-firebase-hooks/util", "main": "dist/index.cjs.js", - "module": "dist/index.esm.js" + "module": "dist/index.esm.js", + "typings": "dist/util/index.d.ts" } From 390fa340ec1466ac3f0687cefea355a8d8782409 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 13:44:56 +0000 Subject: [PATCH 23/29] [messaging] Add VAPID key support --- messaging/README.md | 3 ++- messaging/useToken.ts | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/messaging/README.md b/messaging/README.md index 2e23f98..978de88 100644 --- a/messaging/README.md +++ b/messaging/README.md @@ -18,7 +18,7 @@ List of Cloud Messaging hooks: ### useToken ```js -const [token, loading, error] = useToken(messaging); +const [token, loading, error] = useToken(messaging, vapidKey); ``` Get a token from Firebase Cloud Messaging @@ -26,6 +26,7 @@ Get a token from Firebase Cloud Messaging The `useToken` hook takes the following parameters: - `messaging`: `messaging.Messaging` instance for your Firebase app +- `vapidKey`: a `string` representing the VAPID key credential needed for Cloud Messaging Returns: diff --git a/messaging/useToken.ts b/messaging/useToken.ts index 1817c26..f639edf 100644 --- a/messaging/useToken.ts +++ b/messaging/useToken.ts @@ -4,14 +4,14 @@ import { LoadingHook, useLoadingValue } from '../util'; export type TokenHook = LoadingHook; -export default (messaging: Messaging): TokenHook => { +export default (messaging: Messaging, vapidKey?: string): TokenHook => { const { error, loading, setError, setValue, value } = useLoadingValue< string | null, Error >(); useEffect(() => { - getToken(messaging).then(setValue).catch(setError); + getToken(messaging, { vapidKey }).then(setValue).catch(setError); }, [messaging]); const resArray: TokenHook = [value, loading, error]; diff --git a/package.json b/package.json index 5246a39..58022f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "5.0.0-alpha.3", + "version": "5.0.0-alpha.4", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", From 8746c2300b371b9ded5db5af2c35c3d1a51a7255 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 14:15:40 +0000 Subject: [PATCH 24/29] [functions] Fix loading flag --- auth/README.md | 10 +++++----- functions/README.md | 2 +- functions/useHttpsCallable.ts | 2 +- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/auth/README.md b/auth/README.md index e947caf..d44b318 100644 --- a/auth/README.md +++ b/auth/README.md @@ -499,7 +499,7 @@ const UpdateEmail = () => { /> +

+ + ) +} +``` diff --git a/storage/index.ts b/storage/index.ts index 9db554e..d8e2a13 100644 --- a/storage/index.ts +++ b/storage/index.ts @@ -1 +1,2 @@ export { default as useDownloadURL, DownloadURLHook } from './useDownloadURL'; +export { default as useUploadFile, UploadFileHook } from './useUploadFile'; diff --git a/storage/useUploadFile.ts b/storage/useUploadFile.ts new file mode 100644 index 0000000..88427aa --- /dev/null +++ b/storage/useUploadFile.ts @@ -0,0 +1,59 @@ +import { + StorageError, + StorageReference, + uploadBytesResumable, + UploadMetadata, + UploadResult, + UploadTaskSnapshot, +} from 'firebase/storage'; +import { useMemo, useState } from 'react'; + +export type UploadFileHook = [ + ( + storageRef: StorageReference, + data: Blob | Uint8Array | ArrayBuffer, + metadata?: UploadMetadata | undefined + ) => Promise, + boolean, + UploadTaskSnapshot | undefined, + StorageError | undefined +]; + +export default (): UploadFileHook => { + const [error, setError] = useState(); + const [uploading, setUploading] = useState(false); + const [snapshot, setSnapshot] = useState(); + + const uploadFile = async ( + storageRef: StorageReference, + data: Blob | Uint8Array | ArrayBuffer, + metadata?: UploadMetadata | undefined + ): Promise => { + return new Promise((resolve, reject) => { + setUploading(true); + const uploadTask = uploadBytesResumable(storageRef, data, metadata); + uploadTask.on( + 'state_changed', + (snapshot) => { + setSnapshot(snapshot); + }, + (error) => { + setUploading(false); + setError(error); + resolve(undefined); + }, + () => { + setUploading(false); + setSnapshot(undefined); + resolve({ + metadata: uploadTask.snapshot.metadata, + ref: uploadTask.snapshot.ref, + }); + } + ); + }); + }; + + const resArray: UploadFileHook = [uploadFile, uploading, snapshot, error]; + return useMemo(() => resArray, resArray); +}; From 27f987862d20771e8a6ddbc42d03954f163428ad Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 15:20:42 +0000 Subject: [PATCH 26/29] [typings] Remove flow typings --- auth/index.js.flow | 7 ---- database/index.js.flow | 26 -------------- firestore/index.js.flow | 75 ----------------------------------------- package.json | 6 ---- storage/index.js.flow | 7 ---- 5 files changed, 121 deletions(-) delete mode 100644 auth/index.js.flow delete mode 100644 database/index.js.flow delete mode 100644 firestore/index.js.flow delete mode 100644 storage/index.js.flow diff --git a/auth/index.js.flow b/auth/index.js.flow deleted file mode 100644 index 2ee9b8a..0000000 --- a/auth/index.js.flow +++ /dev/null @@ -1,7 +0,0 @@ -// @flow -import type { Auth, AuthError, User } from 'firebase/auth'; - -type LoadingHook = [T | void, boolean, AuthError | void]; - -export type AuthStateHook = LoadingHook; -declare export function useAuthState(auth: Auth): AuthStateHook; diff --git a/database/index.js.flow b/database/index.js.flow deleted file mode 100644 index 948c997..0000000 --- a/database/index.js.flow +++ /dev/null @@ -1,26 +0,0 @@ -// @flow -import type { DataSnapshot, Query } from 'firebase/database'; - -type LoadingHook = [T | void, boolean, Error | void]; - -export type ListHook = LoadingHook; -export type ListKeysHook = LoadingHook; -export type ListValsHook = LoadingHook; -export type ObjectHook = LoadingHook; -export type ObjectValHook = LoadingHook; - -declare export function useList(query?: Query | null): ListHook; -declare export function useListKeys(query?: Query | null): ListKeysHook; -declare export function useListVals( - query?: Query | null, - options?: { - keyField?: string, - } -): ListValsHook; -declare export function useObject(query?: Query | null): ObjectHook; -declare export function useObjectVal( - query?: Query | null, - options?: { - keyField?: string, - } -): ObjectValHook; diff --git a/firestore/index.js.flow b/firestore/index.js.flow deleted file mode 100644 index c7ca114..0000000 --- a/firestore/index.js.flow +++ /dev/null @@ -1,75 +0,0 @@ -// @flow -import type { - DocumentReference, - DocumentSnapshot, - Query, - QuerySnapshot, - SnapshotListenOptions, -} from 'firebase/firestore'; - -type LoadingHook = [T | void, boolean, Error | void]; - -export type GetOptions = { - source?: 'default' | 'server' | 'cache'; -}; -export type CollectionHook = LoadingHook; -export type CollectionDataHook = LoadingHook; -export type DocumentHook = LoadingHook; -export type DocumentDataHook = LoadingHook; - -declare export function useCollection( - query?: Query | null, - options?: { - snapshotListenOptions?: SnapshotListenOptions, - } -): CollectionHook; -declare export function useCollectionOnce( - query?: Query | null, - options?: { - getOptions?: GetOptions, - } -): CollectionHook; -declare export function useCollectionData( - query?: Query | null, - options?: { - idField?: string, - refField?: string, - snapshotListenOptions?: SnapshotListenOptions, - } -): CollectionDataHook; -declare export function useCollectionDataOnce( - query?: Query | null, - options?: { - getOptions?: GetOptions, - idField?: string, - refField?: string, - } -): CollectionDataHook; -declare export function useDocument( - ref?: DocumentReference | null, - options?: { - snapshotListenOptions?: SnapshotListenOptions, - } -): DocumentHook; -declare export function useDocumentOnce( - ref?: DocumentReference | null, - options?: { - getOptions?: GetOptions, - } -): DocumentHook; -declare export function useDocumentData( - ref?: DocumentReference | null, - options?: { - idField?: string, - refField?: string, - snapshotListenOptions?: SnapshotListenOptions, - } -): DocumentDataHook; -declare export function useDocumentDataOnce( - ref?: DocumentReference | null, - options?: { - getOptions?: GetOptions, - idField?: string, - refField?: string, - } -): DocumentDataHook; diff --git a/package.json b/package.json index d8dc6da..418e532 100644 --- a/package.json +++ b/package.json @@ -15,34 +15,28 @@ "auth/dist/*.js", "auth/dist/auth", "auth/dist/util", - "auth/dist/*.js.flow", "auth/package.json", "database/dist/*.js", "database/dist/database", "database/dist/util", - "database/dist/*.js.flow", "database/package.json", "dist/*.js", "dist/*.js.map", "firestore/dist/*.js", "firestore/dist/firestore", "firestore/dist/util", - "firestore/dist/*.js.flow", "firestore/package.json", "functions/dist/*.js", "functions/dist/functions", "functions/dist/util", - "functions/dist/*.js.flow", "functions/package.json", "messaging/dist/*.js", "messaging/dist/messaging", "messaging/dist/util", - "messaging/dist/*.js.flow", "messaging/package.json", "storage/dist/*.js", "storage/dist/storage", "storage/dist/util", - "storage/dist/*.js.flow", "storage/package.json", "util/dist/*.js", "util/dist/util", diff --git a/storage/index.js.flow b/storage/index.js.flow deleted file mode 100644 index 2624cbe..0000000 --- a/storage/index.js.flow +++ /dev/null @@ -1,7 +0,0 @@ -// @flow -import type { Reference, StorageError } from 'firebase/storage'; - -type LoadingHook = [T | void, boolean, StorageError | void]; - -export type DownloadURLHook = LoadingHook; -declare export function useDownloadURL(ref?: Reference | null): DownloadURLHook; From 7ce1965c47019a17f5670c733d1d62c0d11aaa64 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 15:24:31 +0000 Subject: [PATCH 27/29] 5.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a199b0..ee895db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "5.0.0-alpha.0", + "version": "5.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 418e532..23cb76e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "5.0.0-alpha.7", + "version": "5.0.0", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", From 11635db1b2f82c2c4d700f1bb335babccfb3efd5 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 15:31:49 +0000 Subject: [PATCH 28/29] Fix security warning --- package-lock.json | 150 ++++++++++++++++++++++++++++++++-------------- package.json | 2 +- rollup.config.js | 4 +- 3 files changed, 108 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index ee895db..6700cae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1072,6 +1072,12 @@ "fill-range": "^7.0.1" } }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1141,6 +1147,12 @@ "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1546,27 +1558,6 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1858,6 +1849,15 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "react": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", @@ -1926,6 +1926,55 @@ "is-plain-object": "^3.0.0" } }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "rollup-plugin-typescript2": { "version": "0.31.1", "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.31.1.tgz", @@ -1978,18 +2027,6 @@ } } }, - "rollup-plugin-uglify": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.4.tgz", - "integrity": "sha512-ddgqkH02klveu34TF0JqygPwZnsbhHVI6t8+hGTcYHngPkQb5MIHI0XiztXIN/d6V9j+efwHAqEL7LspSxQXGw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "jest-worker": "^24.0.0", - "serialize-javascript": "^2.1.2", - "uglify-js": "^3.4.9" - } - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2034,12 +2071,6 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -2052,6 +2083,30 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", @@ -2102,6 +2157,17 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "terser": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + } + }, "tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -2167,12 +2233,6 @@ "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true }, - "uglify-js": { - "version": "3.14.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", - "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", - "dev": true - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/package.json b/package.json index 23cb76e..71eca46 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,8 @@ "rimraf": "^2.6.2", "rollup": "^2.63.0", "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.1", - "rollup-plugin-uglify": "^6.0.4", "tslib": "^2.3.1", "typescript": "^4.5.4" }, diff --git a/rollup.config.js b/rollup.config.js index 17383c5..4cf289e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,8 +2,8 @@ import { resolve } from 'path'; import commonjs from '@rollup/plugin-commonjs'; import copy from 'rollup-plugin-copy'; import resolveModule from '@rollup/plugin-node-resolve'; +import { terser } from 'rollup-plugin-terser'; import typescript from 'rollup-plugin-typescript2'; -import { uglify } from 'rollup-plugin-uglify'; import pkg from './package.json'; import authPkg from './auth/package.json'; @@ -80,7 +80,7 @@ export default components }, plugins: [ ...plugins, - uglify(), + terser(), // Copy flow files copy({ [`${component}/index.js.flow`]: `${component}/dist/index.cjs.js.flow`, From 70cf3fc12cdc1bc7842526868eea09b329e13c77 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Fri, 21 Jan 2022 15:32:07 +0000 Subject: [PATCH 29/29] 5.0.0