Skip to content

Commit 01b08aa

Browse files
committed
add initialize() to Provider
1 parent 135e57a commit 01b08aa

File tree

3 files changed

+81
-14
lines changed

3 files changed

+81
-14
lines changed

packages/component/src/provider.test.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,34 @@ describe('Provider', () => {
101101
expect(() => provider.setComponent(component)).to.not.throw();
102102
});
103103

104+
describe('initialize()', () => {
105+
it('throws if the provider is already initialized', () => {
106+
provider.setComponent(getFakeComponent('test', () => ({})));
107+
provider.initialize();
108+
109+
expect(() => provider.initialize()).to.throw();
110+
});
111+
112+
it('throws if the component has not been registered', () => {
113+
expect(() => provider.initialize()).to.throw();
114+
});
115+
116+
it('accepts an options parameter and passes it to the instance factory', () => {
117+
const options = {
118+
configurable: true,
119+
test: true
120+
};
121+
provider.setComponent(
122+
getFakeComponent('test', (_container, opts) => ({
123+
options: opts.options
124+
}))
125+
);
126+
const instance = provider.initialize({ options });
127+
128+
expect((instance as any).options).to.deep.equal(options);
129+
});
130+
});
131+
104132
describe('Provider (multipleInstances = false)', () => {
105133
describe('getImmediate()', () => {
106134
it('throws if the service is not available', () => {
@@ -155,7 +183,7 @@ describe('Provider', () => {
155183
});
156184
});
157185

158-
describe('provideFactory()', () => {
186+
describe('setComponent()', () => {
159187
it('instantiates the service if there is a pending promise and the service is eager', () => {
160188
// create a pending promise
161189
// eslint-disable-next-line @typescript-eslint/no-floating-promises
@@ -320,7 +348,7 @@ describe('Provider', () => {
320348
});
321349
});
322350

323-
describe('provideFactory()', () => {
351+
describe('setComponent()', () => {
324352
it('instantiates services for the pending promises for all instance identifiers', async () => {
325353
/* eslint-disable @typescript-eslint/no-floating-promises */
326354
// create 3 promises for 3 different identifiers

packages/component/src/provider.ts

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
import { Deferred } from '@firebase/util';
1919
import { ComponentContainer } from './component_container';
2020
import { DEFAULT_ENTRY_NAME } from './constants';
21-
import { InstantiationMode, Name, NameServiceMapping } from './types';
21+
import {
22+
InitializeOptions,
23+
InstantiationMode,
24+
Name,
25+
NameServiceMapping
26+
} from './types';
2227
import { Component } from './component';
2328

2429
/**
@@ -51,7 +56,9 @@ export class Provider<T extends Name> {
5156
this.instancesDeferred.set(normalizedIdentifier, deferred);
5257
// If the service instance is available, resolve the promise with it immediately
5358
try {
54-
const instance = this.getOrInitializeService(normalizedIdentifier);
59+
const instance = this.getOrInitializeService({
60+
instanceIdentifier: normalizedIdentifier
61+
});
5562
if (instance) {
5663
deferred.resolve(instance);
5764
}
@@ -92,7 +99,9 @@ export class Provider<T extends Name> {
9299
// if multipleInstances is not supported, use the default name
93100
const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);
94101
try {
95-
const instance = this.getOrInitializeService(normalizedIdentifier);
102+
const instance = this.getOrInitializeService({
103+
instanceIdentifier: normalizedIdentifier
104+
});
96105

97106
if (!instance) {
98107
if (optional) {
@@ -129,7 +138,7 @@ export class Provider<T extends Name> {
129138
// if the service is eager, initialize the default instance
130139
if (isComponentEager(component)) {
131140
try {
132-
this.getOrInitializeService(DEFAULT_ENTRY_NAME);
141+
this.getOrInitializeService({ instanceIdentifier: DEFAULT_ENTRY_NAME });
133142
} catch (e) {
134143
// when the instance factory for an eager Component throws an exception during the eager
135144
// initialization, it should not cause a fatal error.
@@ -151,7 +160,9 @@ export class Provider<T extends Name> {
151160

152161
try {
153162
// `getOrInitializeService()` should always return a valid instance since a component is guaranteed. use ! to make typescript happy.
154-
const instance = this.getOrInitializeService(normalizedIdentifier)!;
163+
const instance = this.getOrInitializeService({
164+
instanceIdentifier: normalizedIdentifier
165+
})!;
155166
instanceDeferred.resolve(instance);
156167
} catch (e) {
157168
// when the instance factory throws an exception, it should not cause
@@ -190,15 +201,41 @@ export class Provider<T extends Name> {
190201
return this.instances.has(identifier);
191202
}
192203

193-
private getOrInitializeService(
194-
identifier: string
195-
): NameServiceMapping[T] | null {
196-
let instance = this.instances.get(identifier);
204+
initialize(opts: InitializeOptions = {}): NameServiceMapping[T] {
205+
const { instanceIdentifier = DEFAULT_ENTRY_NAME, options = {} } = opts;
206+
const normalizedIdentifier = this.normalizeInstanceIdentifier(
207+
instanceIdentifier
208+
);
209+
if (this.isInitialized(normalizedIdentifier)) {
210+
throw Error(
211+
`${this.name}(${normalizedIdentifier}) has already been initialized`
212+
);
213+
}
214+
215+
if (!this.isComponentSet()) {
216+
throw Error(`Component ${this.name} has not been registered yet`);
217+
}
218+
219+
return this.getOrInitializeService({
220+
instanceIdentifier: normalizedIdentifier,
221+
options
222+
})!;
223+
}
224+
225+
private getOrInitializeService({
226+
instanceIdentifier,
227+
options = {}
228+
}: {
229+
instanceIdentifier: string;
230+
options?: Record<string, unknown>;
231+
}): NameServiceMapping[T] | null {
232+
let instance = this.instances.get(instanceIdentifier);
197233
if (!instance && this.component) {
198234
instance = this.component.instanceFactory(this.container, {
199-
instanceIdentifier: normalizeIdentifierForFactory(identifier)
200-
}) as NameServiceMapping[T];
201-
this.instances.set(identifier, instance);
235+
instanceIdentifier: normalizeIdentifierForFactory(instanceIdentifier),
236+
options
237+
});
238+
this.instances.set(instanceIdentifier, instance);
202239
}
203240

204241
return instance || null;

packages/component/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export interface InstanceFactoryOptions {
4141
options?: Record<string, unknown>;
4242
}
4343

44+
export type InitializeOptions = InstanceFactoryOptions;
45+
4446
/**
4547
* Factory to create an instance of type T, given a ComponentContainer.
4648
* ComponentContainer is the IOC container that provides {@link Provider}

0 commit comments

Comments
 (0)