mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-05-28 15:44:46 +08:00
297 lines
8.2 KiB
JavaScript
297 lines
8.2 KiB
JavaScript
/*
|
|
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this library except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
import { isString } from '@react-native-firebase/app/lib/common';
|
|
import FirebaseApp from '../../FirebaseApp';
|
|
import SDK_VERSION from '../../version';
|
|
import { DEFAULT_APP_NAME, KNOWN_NAMESPACES } from '../constants';
|
|
import FirebaseModule from '../FirebaseModule';
|
|
import { getApp, getApps, initializeApp, setOnAppCreate, setOnAppDestroy } from './app';
|
|
|
|
// firebase.X
|
|
let FIREBASE_ROOT = null;
|
|
|
|
const NAMESPACE_REGISTRY = {};
|
|
const APP_MODULE_INSTANCE = {};
|
|
const MODULE_GETTER_FOR_APP = {};
|
|
const MODULE_GETTER_FOR_ROOT = {};
|
|
|
|
/**
|
|
* Attaches module namespace getters on every newly created app.
|
|
*
|
|
* Structured like this to avoid metro require cycles.
|
|
*/
|
|
setOnAppCreate(app => {
|
|
for (let i = 0; i < KNOWN_NAMESPACES.length; i++) {
|
|
const moduleNamespace = KNOWN_NAMESPACES[i];
|
|
Object.defineProperty(app, moduleNamespace, {
|
|
enumerable: false,
|
|
get: firebaseAppModuleProxy.bind(null, app, moduleNamespace),
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Destroys all APP_MODULE_INSTANCE & MODULE_GETTER_FOR_APP objects relating to the
|
|
* recently destroyed app.
|
|
*
|
|
* Structured like this to avoid metro require cycles.
|
|
*/
|
|
setOnAppDestroy(app => {
|
|
delete APP_MODULE_INSTANCE[app.name];
|
|
delete MODULE_GETTER_FOR_APP[app.name];
|
|
});
|
|
|
|
/**
|
|
*
|
|
* @param app
|
|
* @param moduleNamespace
|
|
* @returns {*}
|
|
*/
|
|
function getOrCreateModuleForApp(app, moduleNamespace) {
|
|
if (MODULE_GETTER_FOR_APP[app.name] && MODULE_GETTER_FOR_APP[app.name][moduleNamespace]) {
|
|
return MODULE_GETTER_FOR_APP[app.name][moduleNamespace];
|
|
}
|
|
|
|
if (!MODULE_GETTER_FOR_APP[app.name]) {
|
|
MODULE_GETTER_FOR_APP[app.name] = {};
|
|
}
|
|
|
|
const { hasCustomUrlOrRegionSupport, hasMultiAppSupport, ModuleClass } = NAMESPACE_REGISTRY[
|
|
moduleNamespace
|
|
];
|
|
|
|
// modules such as analytics only run on the default app
|
|
if (!hasMultiAppSupport && app.name !== DEFAULT_APP_NAME) {
|
|
throw new Error(
|
|
[
|
|
`You attempted to call "firebase.app('${
|
|
app.name
|
|
}').${moduleNamespace}" but; ${moduleNamespace} does not support multiple Firebase Apps.`,
|
|
'',
|
|
`Ensure you access ${moduleNamespace} from the default application only.`,
|
|
].join('\r\n'),
|
|
);
|
|
}
|
|
|
|
// e.g. firebase.storage(customUrlOrRegion)
|
|
function firebaseModuleWithArgs(customUrlOrRegion) {
|
|
if (customUrlOrRegion !== undefined) {
|
|
if (!hasCustomUrlOrRegionSupport) {
|
|
// TODO throw Module does not support arguments error
|
|
}
|
|
|
|
if (!isString(customUrlOrRegion)) {
|
|
// TODO throw Module first argument must be a string error
|
|
}
|
|
}
|
|
|
|
const key = customUrlOrRegion ? `${customUrlOrRegion}:${moduleNamespace}` : moduleNamespace;
|
|
|
|
if (!APP_MODULE_INSTANCE[app.name]) {
|
|
APP_MODULE_INSTANCE[app.name] = {};
|
|
}
|
|
|
|
if (!APP_MODULE_INSTANCE[app.name][key]) {
|
|
APP_MODULE_INSTANCE[app.name][key] = new ModuleClass(
|
|
app,
|
|
NAMESPACE_REGISTRY[moduleNamespace],
|
|
customUrlOrRegion,
|
|
);
|
|
}
|
|
|
|
return APP_MODULE_INSTANCE[app.name][key];
|
|
}
|
|
|
|
MODULE_GETTER_FOR_APP[app.name][moduleNamespace] = firebaseModuleWithArgs;
|
|
return MODULE_GETTER_FOR_APP[app.name][moduleNamespace];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param moduleNamespace
|
|
* @returns {*}
|
|
*/
|
|
function getOrCreateModuleForRoot(moduleNamespace) {
|
|
if (MODULE_GETTER_FOR_ROOT[moduleNamespace]) {
|
|
return MODULE_GETTER_FOR_ROOT[moduleNamespace];
|
|
}
|
|
|
|
const { statics, hasMultiAppSupport, ModuleClass } = NAMESPACE_REGISTRY[moduleNamespace];
|
|
|
|
// e.g. firebase.storage(app)
|
|
function firebaseModuleWithApp(app) {
|
|
const _app = app || getApp();
|
|
|
|
if (!(_app instanceof FirebaseApp)) {
|
|
throw new Error(
|
|
[
|
|
`"firebase.${moduleNamespace}(app)" arg expects a FirebaseApp instance or undefined.`,
|
|
'',
|
|
'Ensure the arg provided is a Firebase app instance; or no args to use the default Firebase app.',
|
|
].join('\r\n'),
|
|
);
|
|
}
|
|
|
|
// modules such as analytics only run on the default app
|
|
if (!hasMultiAppSupport && _app.name !== DEFAULT_APP_NAME) {
|
|
throw new Error(
|
|
[
|
|
`You attempted to call "firebase.${moduleNamespace}(app)" but; ${moduleNamespace} does not support multiple Firebase Apps.`,
|
|
'',
|
|
`Ensure the app provided is the default Firebase app only and not the "${
|
|
_app.name
|
|
}" app.`,
|
|
].join('\r\n'),
|
|
);
|
|
}
|
|
|
|
if (!APP_MODULE_INSTANCE[_app.name]) {
|
|
APP_MODULE_INSTANCE[_app.name] = {};
|
|
}
|
|
|
|
if (!APP_MODULE_INSTANCE[_app.name][moduleNamespace]) {
|
|
APP_MODULE_INSTANCE[_app.name][moduleNamespace] = new ModuleClass(
|
|
_app,
|
|
NAMESPACE_REGISTRY[moduleNamespace],
|
|
);
|
|
}
|
|
|
|
return APP_MODULE_INSTANCE[_app.name][moduleNamespace];
|
|
}
|
|
|
|
Object.assign(firebaseModuleWithApp, statics || {});
|
|
Object.freeze(firebaseModuleWithApp);
|
|
MODULE_GETTER_FOR_ROOT[moduleNamespace] = firebaseModuleWithApp;
|
|
|
|
return MODULE_GETTER_FOR_ROOT[moduleNamespace];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param firebaseNamespace
|
|
* @param moduleNamespace
|
|
* @returns {*}
|
|
*/
|
|
function firebaseRootModuleProxy(firebaseNamespace, moduleNamespace) {
|
|
if (NAMESPACE_REGISTRY[moduleNamespace]) {
|
|
return getOrCreateModuleForRoot(moduleNamespace);
|
|
}
|
|
|
|
throw new Error(
|
|
[
|
|
`You attempted to use 'firebase.${moduleNamespace}' but this module could not be found.`,
|
|
'',
|
|
`Ensure you have installed and imported the '@react-native-firebase/${moduleNamespace}' package.`,
|
|
].join('\r\n'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param app
|
|
* @param moduleNamespace
|
|
* @returns {*}
|
|
*/
|
|
export function firebaseAppModuleProxy(app, moduleNamespace) {
|
|
if (NAMESPACE_REGISTRY[moduleNamespace]) {
|
|
app._checkDestroyed();
|
|
return getOrCreateModuleForApp(app, moduleNamespace);
|
|
}
|
|
|
|
throw new Error(
|
|
[
|
|
`You attempted to use "firebase.app('${
|
|
app.name
|
|
}').${moduleNamespace}" but this module could not be found.`,
|
|
'',
|
|
`Ensure you have installed and imported the '@react-native-firebase/${moduleNamespace}' package.`,
|
|
].join('\r\n'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {*}
|
|
*/
|
|
export function createFirebaseRoot() {
|
|
FIREBASE_ROOT = {
|
|
initializeApp,
|
|
get app() {
|
|
return getApp;
|
|
},
|
|
get apps() {
|
|
return getApps();
|
|
},
|
|
SDK_VERSION,
|
|
};
|
|
|
|
for (let i = 0; i < KNOWN_NAMESPACES.length; i++) {
|
|
const namespace = KNOWN_NAMESPACES[i];
|
|
Object.defineProperty(FIREBASE_ROOT, namespace, {
|
|
enumerable: false,
|
|
get: firebaseRootModuleProxy.bind(null, FIREBASE_ROOT, namespace),
|
|
});
|
|
}
|
|
|
|
return Object.freeze(FIREBASE_ROOT);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {*}
|
|
*/
|
|
export function getFirebaseRoot() {
|
|
if (FIREBASE_ROOT) {
|
|
return FIREBASE_ROOT;
|
|
}
|
|
return createFirebaseRoot();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param options
|
|
* @returns {*}
|
|
*/
|
|
export function createModuleNamespace(options = {}) {
|
|
const { namespace, ModuleClass, version } = options;
|
|
|
|
if (!NAMESPACE_REGISTRY[namespace]) {
|
|
// validation only for internal / module dev usage
|
|
// instanceof does not work in build
|
|
if (FirebaseModule.__extended__ !== ModuleClass.__extended__) {
|
|
throw new Error('INTERNAL ERROR: ModuleClass must be an instance of FirebaseModule.');
|
|
}
|
|
|
|
if (version !== SDK_VERSION) {
|
|
throw new Error(
|
|
[
|
|
`You've attempted to require '@react-native-firebase/${namespace}' version '${version}', ` +
|
|
`however, the '@react-native-firebase/app' module is of a different version (${SDK_VERSION}).`,
|
|
'',
|
|
'All React Native Firebase modules must be of the same version. Please ensure they match up ' +
|
|
'in your package.json file and re-run yarn/npm install.',
|
|
].join('\n'),
|
|
);
|
|
}
|
|
|
|
NAMESPACE_REGISTRY[namespace] = Object.assign({}, options);
|
|
}
|
|
|
|
return getFirebaseRoot()[namespace];
|
|
}
|