Skip to content

Commit bbbee88

Browse files
committed
Component framework implementation (#2316)
* Component implementation * update dep version * [AUTOMATED]: Prettier Code Styling * [AUTOMATED]: License Headers * rename variables * address comments * [AUTOMATED]: Prettier Code Styling * remove unused comment * update code * [AUTOMATED]: Prettier Code Styling * rename to clearInstance to have naming consistency * make FirebaseApp tests work again * fix node tests * [AUTOMATED]: Prettier Code Styling * add comments for ComponentType * [AUTOMATED]: Prettier Code Styling * pass Component directly into Providers * [AUTOMATED]: Prettier Code Styling * correct spellings * update readme * fix lint issue * remove unused import * fix API change * move types around * [AUTOMATED]: Prettier Code Styling * improve provider typing * [AUTOMATED]: Prettier Code Styling * Migrate analytics to component platform (#220) * migrate analytics * minor analytics refactoring * interface merge * [AUTOMATED]: Prettier Code Styling * [AUTOMATED]: License Headers * allow overwriting a registered component * [AUTOMATED]: Prettier Code Styling * change ComponentType to string enum * address comments * [AUTOMATED]: Prettier Code Styling * remove return only generics * Move identifier to options object for getImmediate() * [AUTOMATED]: Prettier Code Styling * Make getProvider() type safe * [AUTOMATED]: Prettier Code Styling * define a new method to replace overwrite flag * [AUTOMATED]: Prettier Code Styling * Make component type safe * [AUTOMATED]: Prettier Code Styling * remove the generic type from component container * Update FirebaseApp and Analytics * [AUTOMATED]: Prettier Code Styling * remove unneccessary casting * [AUTOMATED]: Prettier Code Styling * fix typo * address comments * [AUTOMATED]: Prettier Code Styling * update some types * [AUTOMATED]: Prettier Code Styling * handle errors from instance factory * [AUTOMATED]: Prettier Code Styling
1 parent ba1932f commit bbbee88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1792
-660
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# @firebase/analytics-interop-types
2+
3+
**This package is not intended for direct usage, and should only be used via the officially supported [firebase](https://www.npmjs.com/package/firebase) package.**
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export interface FirebaseAnalyticsInternal {
19+
/**
20+
* Sends analytics event with given `eventParams`. This method
21+
* automatically associates this logged event with this Firebase web
22+
* app instance on this device.
23+
* List of official event parameters can be found in
24+
* {@link https://developers.google.com/gtagjs/reference/event
25+
* the gtag.js reference documentation}.
26+
*/
27+
logEvent(
28+
eventName: string,
29+
eventParams?: { [key: string]: unknown },
30+
options?: AnalyticsCallOptions
31+
): void;
32+
}
33+
34+
export interface AnalyticsCallOptions {
35+
/**
36+
* If true, this config or event call applies globally to all
37+
* analytics properties on the page.
38+
*/
39+
global: boolean;
40+
}
41+
42+
declare module '@firebase/component' {
43+
interface NameServiceMapping {
44+
'analytics-internal': FirebaseAnalyticsInternal;
45+
}
46+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@firebase/analytics-interop-types",
3+
"version": "0.1.0",
4+
"description": "@firebase/analytics Types",
5+
"author": "Firebase <[email protected]> (https://firebase.google.com/)",
6+
"license": "Apache-2.0",
7+
"scripts": {
8+
"test": "tsc"
9+
},
10+
"files": [
11+
"index.d.ts"
12+
],
13+
"repository": {
14+
"directory": "packages/analytics-interop-types",
15+
"type": "git",
16+
"url": "https://github.com/firebase/firebase-js-sdk.git"
17+
},
18+
"bugs": {
19+
"url": "https://github.com/firebase/firebase-js-sdk/issues"
20+
},
21+
"devDependencies": {
22+
"typescript": "3.6.4"
23+
}
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../config/tsconfig.base.json",
3+
"compilerOptions": {
4+
"noEmit": true
5+
},
6+
"exclude": [
7+
"dist/**/*"
8+
]
9+
}

packages/analytics-types/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,9 @@ export interface Promotion {
201201
id?: string;
202202
name?: string;
203203
}
204+
205+
declare module '@firebase/component' {
206+
interface NameServiceMapping {
207+
'analytics': FirebaseAnalytics;
208+
}
209+
}

packages/analytics/index.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ const customDataLayerName = 'customDataLayer';
3939
describe('FirebaseAnalytics instance tests', () => {
4040
it('Throws if no analyticsId in config', () => {
4141
const app = getFakeApp();
42-
expect(() => analyticsFactory(app, () => {})).to.throw('field is empty');
42+
expect(() => analyticsFactory(app)).to.throw('field is empty');
4343
});
4444
it('Throws if creating an instance with already-used analytics ID', () => {
4545
const app = getFakeApp(analyticsId);
4646
resetGlobalVars(false, { [analyticsId]: Promise.resolve() });
47-
expect(() => analyticsFactory(app, () => {})).to.throw('already exists');
47+
expect(() => analyticsFactory(app)).to.throw('already exists');
4848
});
4949
describe('Standard app, page already has user gtag script', () => {
5050
let app: FirebaseApp = {} as FirebaseApp;
@@ -53,7 +53,7 @@ describe('FirebaseAnalytics instance tests', () => {
5353
app = getFakeApp(analyticsId);
5454
window['gtag'] = gtagStub;
5555
window['dataLayer'] = [];
56-
analyticsInstance = analyticsFactory(app, () => {});
56+
analyticsInstance = analyticsFactory(app);
5757
});
5858
after(() => {
5959
delete window['gtag'];
@@ -119,7 +119,7 @@ describe('FirebaseAnalytics instance tests', () => {
119119
dataLayerName: customDataLayerName,
120120
gtagName: customGtagName
121121
});
122-
analyticsInstance = analyticsFactory(app, () => {});
122+
analyticsInstance = analyticsFactory(app);
123123
});
124124
after(() => {
125125
delete window[customGtagName];
@@ -162,7 +162,7 @@ describe('FirebaseAnalytics instance tests', () => {
162162
before(() => {
163163
resetGlobalVars();
164164
const app = getFakeApp(analyticsId);
165-
analyticsInstance = analyticsFactory(app, () => {});
165+
analyticsInstance = analyticsFactory(app);
166166
});
167167
after(() => {
168168
delete window['gtag'];

packages/analytics/index.ts

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
*/
1717
import firebase from '@firebase/app';
1818
import { FirebaseAnalytics } from '@firebase/analytics-types';
19-
import {
20-
FirebaseServiceFactory,
21-
_FirebaseNamespace
22-
} from '@firebase/app-types/private';
19+
import { FirebaseAnalyticsInternal } from '@firebase/analytics-interop-types';
20+
import { _FirebaseNamespace } from '@firebase/app-types/private';
2321
import { factory, settings, resetGlobalVars } from './src/factory';
2422
import { EventName } from './src/constants';
23+
import { Component, ComponentType } from '@firebase/component';
24+
import { ERROR_FACTORY, AnalyticsError } from './src/errors';
2525

2626
declare global {
2727
interface Window {
@@ -34,17 +34,41 @@ declare global {
3434
*/
3535
const ANALYTICS_TYPE = 'analytics';
3636
export function registerAnalytics(instance: _FirebaseNamespace): void {
37-
instance.INTERNAL.registerService(
38-
ANALYTICS_TYPE,
39-
factory as FirebaseServiceFactory,
40-
{
37+
instance.INTERNAL.registerComponent(
38+
new Component(
39+
ANALYTICS_TYPE,
40+
container => {
41+
// getImmediate for FirebaseApp will always succeed
42+
const app = container.getProvider('app').getImmediate();
43+
return factory(app);
44+
},
45+
ComponentType.PUBLIC
46+
).setServiceProps({
4147
settings,
4248
EventName
43-
},
44-
// We don't need to wait on any AppHooks.
45-
undefined,
46-
// Allow multiple analytics instances per app.
47-
false
49+
})
50+
);
51+
52+
instance.INTERNAL.registerComponent(
53+
new Component(
54+
'analytics-internal',
55+
container => {
56+
try {
57+
const analytics = container
58+
.getProvider(ANALYTICS_TYPE)
59+
.getImmediate();
60+
return {
61+
logEvent: analytics.logEvent
62+
};
63+
} catch (e) {
64+
throw ERROR_FACTORY.create(
65+
AnalyticsError.INTEROP_COMPONENT_REG_FAILED,
66+
{ reason: e }
67+
);
68+
}
69+
},
70+
ComponentType.PRIVATE
71+
)
4872
);
4973
}
5074

packages/analytics/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@firebase/analytics-types": "0.2.3",
2727
"@firebase/installations": "0.3.6",
2828
"@firebase/util": "0.2.34",
29+
"@firebase/component": "0.1.0",
2930
"tslib": "1.10.0"
3031
},
3132
"license": "Apache-2.0",

packages/analytics/src/errors.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { ANALYTICS_ID_FIELD } from './constants';
2121
export const enum AnalyticsError {
2222
NO_GA_ID = 'no-ga-id',
2323
ALREADY_EXISTS = 'already-exists',
24-
ALREADY_INITIALIZED = 'already-initialized'
24+
ALREADY_INITIALIZED = 'already-initialized',
25+
INTEROP_COMPONENT_REG_FAILED = 'interop-component-reg-failed'
2526
}
2627

2728
const ERRORS: ErrorMap<AnalyticsError> = {
@@ -36,11 +37,14 @@ const ERRORS: ErrorMap<AnalyticsError> = {
3637
[AnalyticsError.ALREADY_INITIALIZED]:
3738
'Firebase Analytics has already been initialized.' +
3839
'settings() must be called before initializing any Analytics instance' +
39-
'or it will have no effect.'
40+
'or it will have no effect.',
41+
[AnalyticsError.INTEROP_COMPONENT_REG_FAILED]:
42+
'Firebase Analytics Interop Component failed to instantiate'
4043
};
4144

4245
interface ErrorParams {
4346
[AnalyticsError.ALREADY_EXISTS]: { id: string };
47+
[AnalyticsError.INTEROP_COMPONENT_REG_FAILED]: { reason: Error };
4448
}
4549

4650
export const ERROR_FACTORY = new ErrorFactory<AnalyticsError, ErrorParams>(

packages/analytics/src/factory.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { FirebaseApp } from '@firebase/app-types';
1918
import {
2019
FirebaseAnalytics,
2120
Gtag,
@@ -38,6 +37,7 @@ import {
3837
} from './helpers';
3938
import { ANALYTICS_ID_FIELD } from './constants';
4039
import { AnalyticsError, ERROR_FACTORY } from './errors';
40+
import { FirebaseApp } from '@firebase/app-types';
4141

4242
/**
4343
* Maps gaId to FID fetch promises.
@@ -102,11 +102,7 @@ export function settings(options: SettingsOptions): void {
102102
}
103103
}
104104

105-
export function factory(
106-
app: FirebaseApp,
107-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108-
extendApp: (props: { [prop: string]: any }) => void
109-
): FirebaseAnalytics {
105+
export function factory(app: FirebaseApp): FirebaseAnalytics {
110106
const analyticsId = app.options[ANALYTICS_ID_FIELD];
111107
if (!analyticsId) {
112108
throw ERROR_FACTORY.create(AnalyticsError.NO_GA_ID);
@@ -161,13 +157,5 @@ export function factory(
161157
setAnalyticsCollectionEnabled(analyticsId, enabled)
162158
};
163159

164-
extendApp({
165-
INTERNAL: {
166-
analytics: {
167-
logEvent: analyticsInstance.logEvent
168-
}
169-
}
170-
});
171-
172160
return analyticsInstance;
173161
}

packages/app-types/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,9 @@ export interface FirebaseNamespace {
101101
// The current SDK version.
102102
SDK_VERSION: string;
103103
}
104+
105+
declare module '@firebase/component' {
106+
interface NameServiceMapping {
107+
'app': FirebaseApp;
108+
}
109+
}

packages/app-types/private.d.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import { FirebaseApp, FirebaseNamespace } from '@firebase/app-types';
2424
import { Observer, Subscribe } from '@firebase/util';
2525
import { FirebaseError, ErrorFactory } from '@firebase/util';
26+
import { Deferred } from '../firestore/test/util/promise';
27+
import { Component } from '@firebase/component';
2628

2729
export interface FirebaseServiceInternals {
2830
/**
@@ -80,8 +82,8 @@ export interface FirebaseAppInternals {
8082
}
8183

8284
export interface _FirebaseApp extends FirebaseApp {
83-
INTERNAL: FirebaseAppInternals;
84-
_removeServiceInstance: (name: string, instanceIdentifier?: string) => void;
85+
_addComponent(component: Component): void;
86+
_removeServiceInstance(name: string, instanceIdentifier?: string): void;
8587
}
8688
export interface _FirebaseNamespace extends FirebaseNamespace {
8789
INTERNAL: {
@@ -99,13 +101,9 @@ export interface _FirebaseNamespace extends FirebaseNamespace {
99101
* @param allowMultipleInstances Whether the registered service supports
100102
* multiple instances per app. If not specified, the default is false.
101103
*/
102-
registerService(
103-
name: string,
104-
createService: FirebaseServiceFactory,
105-
serviceProperties?: { [prop: string]: any },
106-
appHook?: AppHook,
107-
allowMultipleInstances?: boolean
108-
): FirebaseServiceNamespace<FirebaseService>;
104+
registerComponent(
105+
component: Component
106+
): FirebaseServiceNamespace<FirebaseService> | null;
109107

110108
/**
111109
* Just used for testing to start from a fresh namespace.
@@ -139,9 +137,9 @@ export interface _FirebaseNamespace extends FirebaseNamespace {
139137
removeApp(name: string): void;
140138

141139
/**
142-
* Service factories for each registered service.
140+
* registered components.
143141
*/
144-
factories: { [name: string]: FirebaseServiceFactory };
142+
components: Map<string, Component>;
145143

146144
/*
147145
* Convert service name to factory name to use.

packages/app/index.node.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,14 @@
1717

1818
import { FirebaseNamespace } from '@firebase/app-types';
1919
import { _FirebaseNamespace } from '@firebase/app-types/private';
20-
import { createFirebaseNamespace } from './src/firebaseNamespace';
20+
import { firebase as _firebase } from './src/firebaseNamespace';
2121
// Node specific packages.
2222
// @ts-ignore
2323
import Storage from 'dom-storage';
2424
// @ts-ignore
2525
import { XMLHttpRequest } from 'xmlhttprequest';
2626

27-
const _firebase = createFirebaseNamespace() as _FirebaseNamespace;
28-
29-
_firebase.INTERNAL.extendNamespace({
27+
(_firebase as _FirebaseNamespace).INTERNAL.extendNamespace({
3028
INTERNAL: {
3129
node: {
3230
localStorage: new Storage(null, { strict: true }),

packages/app/index.rn.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
import { FirebaseNamespace } from '@firebase/app-types';
1919
import { _FirebaseNamespace } from '@firebase/app-types/private';
20-
import { createFirebaseNamespace } from './src/firebaseNamespace';
21-
20+
import { firebase as _firebase } from './src/firebaseNamespace';
2221
/**
2322
* To avoid having to include the @types/react-native package, which breaks
2423
* some of our tests because of duplicate symbols, we are using require syntax
@@ -27,9 +26,7 @@ import { createFirebaseNamespace } from './src/firebaseNamespace';
2726
// eslint-disable-next-line @typescript-eslint/no-require-imports
2827
const { AsyncStorage } = require('react-native');
2928

30-
const _firebase = createFirebaseNamespace() as _FirebaseNamespace;
31-
32-
_firebase.INTERNAL.extendNamespace({
29+
(_firebase as _FirebaseNamespace).INTERNAL.extendNamespace({
3330
INTERNAL: {
3431
reactNative: {
3532
AsyncStorage

packages/app/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import { FirebaseNamespace } from '@firebase/app-types';
19-
import { createFirebaseNamespace } from './src/firebaseNamespace';
19+
import { firebase as firebaseNamespace } from './src/firebaseNamespace';
2020
import { isNode, isBrowser } from '@firebase/util';
2121
import { logger } from './src/logger';
2222

@@ -38,7 +38,6 @@ if (isBrowser() && (self as any).firebase !== undefined) {
3838
}
3939
}
4040

41-
const firebaseNamespace = createFirebaseNamespace();
4241
const initializeApp = firebaseNamespace.initializeApp;
4342

4443
// TODO: This disable can be removed and the 'ignoreRestArgs' option added to

0 commit comments

Comments
 (0)