Skip to content

Commit 1824a9f

Browse files
Merge pull request #2262 from underfin/provider-useExisting
feat(core): support useExisting provider #2415
2 parents 6a606b0 + 0ff1612 commit 1824a9f

File tree

4 files changed

+88
-9
lines changed

4 files changed

+88
-9
lines changed

packages/common/interfaces/modules/provider.interface.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export type Provider<T = any> =
66
| Type<any>
77
| ClassProvider<T>
88
| ValueProvider<T>
9-
| FactoryProvider<T>;
9+
| FactoryProvider<T>
10+
| ExistingProvider<T>;
1011

1112
export interface ClassProvider<T = any> {
1213
provide: string | symbol | Type<any> | Abstract<any> | Function;
@@ -25,3 +26,8 @@ export interface FactoryProvider<T = any> {
2526
inject?: Array<Type<any> | string | symbol | Abstract<any> | Function>;
2627
scope?: Scope;
2728
}
29+
30+
export interface ExistingProvider<T = any> {
31+
provide: string | symbol | Type<any> | Abstract<any> | Function;
32+
useExisting: any;
33+
}

packages/core/injector/module.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
NestModule,
1111
Provider,
1212
ValueProvider,
13+
ExistingProvider,
1314
} from '@nestjs/common/interfaces';
1415
import { Type } from '@nestjs/common/interfaces/type.interface';
1516
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
@@ -39,7 +40,7 @@ export class Module {
3940
private readonly _controllers = new Map<
4041
string,
4142
InstanceWrapper<Controller>
42-
>();
43+
>();
4344
private readonly _exports = new Set<string | symbol>();
4445

4546
constructor(
@@ -187,14 +188,14 @@ export class Module {
187188

188189
public isCustomProvider(
189190
provider: Provider,
190-
): provider is ClassProvider | FactoryProvider | ValueProvider {
191+
): provider is ClassProvider | FactoryProvider | ValueProvider | ExistingProvider {
191192
return !isNil(
192-
(provider as ClassProvider | FactoryProvider | ValueProvider).provide,
193+
(provider as ClassProvider | FactoryProvider | ValueProvider | ExistingProvider).provide,
193194
);
194195
}
195196

196197
public addCustomProvider(
197-
provider: (ClassProvider | FactoryProvider | ValueProvider) & ProviderName,
198+
provider: (ClassProvider | FactoryProvider | ValueProvider | ExistingProvider) & ProviderName,
198199
collection: Map<string, any>,
199200
): string {
200201
const name = this.getProviderStaticToken(provider.provide) as string;
@@ -208,6 +209,8 @@ export class Module {
208209
this.addCustomValue(provider, collection);
209210
} else if (this.isCustomFactory(provider)) {
210211
this.addCustomFactory(provider, collection);
212+
} else if (this.isCustomUseExisting(provider)) {
213+
this.addCustomUseExisting(provider, collection);
211214
}
212215
return name;
213216
}
@@ -224,6 +227,10 @@ export class Module {
224227
return !isUndefined((provider as FactoryProvider).useFactory);
225228
}
226229

230+
public isCustomUseExisting(provider: any): provider is ExistingProvider {
231+
return !isUndefined((provider as ExistingProvider).useExisting);
232+
}
233+
227234
public isDynamicModule(exported: any): exported is DynamicModule {
228235
return exported && exported.module;
229236
}
@@ -283,6 +290,24 @@ export class Module {
283290
);
284291
}
285292

293+
public addCustomUseExisting(
294+
provider: ExistingProvider & ProviderName,
295+
collection: Map<string, InstanceWrapper>,
296+
) {
297+
const { name, useExisting } = provider;
298+
collection.set(
299+
name as string,
300+
new InstanceWrapper({
301+
name,
302+
metatype: ((instance) => instance) as any,
303+
instance: null,
304+
isResolved: false,
305+
inject: [useExisting],
306+
host: this,
307+
}),
308+
);
309+
}
310+
286311
public addExportedProvider(
287312
provider: Provider & ProviderName | string | symbol | DynamicModule,
288313
) {
@@ -301,7 +326,7 @@ export class Module {
301326
}
302327

303328
public addCustomExportedProvider(
304-
provider: FactoryProvider | ValueProvider | ClassProvider,
329+
provider: FactoryProvider | ValueProvider | ClassProvider | ExistingProvider,
305330
) {
306331
const provide = provider.provide;
307332
if (isString(provide) || isSymbol(provide)) {

packages/core/scanner.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
ClassProvider,
1717
FactoryProvider,
1818
ValueProvider,
19+
ExistingProvider,
1920
} from '@nestjs/common/interfaces';
2021
import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface';
2122
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
@@ -256,7 +257,11 @@ export class DependenciesScanner {
256257

257258
public isCustomProvider(
258259
provider: Provider,
259-
): provider is ClassProvider | ValueProvider | FactoryProvider {
260+
): provider is
261+
| ClassProvider
262+
| ValueProvider
263+
| FactoryProvider
264+
| ExistingProvider {
260265
return provider && !isNil((provider as any).provide);
261266
}
262267

@@ -267,8 +272,11 @@ export class DependenciesScanner {
267272
}
268273
const applyProvidersMap = this.getApplyProvidersMap();
269274
const providersKeys = Object.keys(applyProvidersMap);
270-
const type = (provider as ClassProvider | ValueProvider | FactoryProvider)
271-
.provide;
275+
const type = (provider as
276+
| ClassProvider
277+
| ValueProvider
278+
| FactoryProvider
279+
| ExistingProvider).provide;
272280

273281
if (!providersKeys.includes(type as string)) {
274282
return this.container.addProvider(provider as any, token);

packages/core/test/injector/module.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ describe('Module', () => {
140140
expect((addCustomFactory as sinon.SinonSpy).called).to.be.true;
141141
});
142142

143+
it('should call "addCustomUseExisting" when "useExisting" property exists', () => {
144+
const addCustomUseExisting = sinon.spy();
145+
module.addCustomUseExisting = addCustomUseExisting;
146+
147+
const provider = { provide: 'test', useExisting: () => null };
148+
149+
module.addCustomUseExisting(provider as any, new Map());
150+
expect((addCustomUseExisting as sinon.SinonSpy).called).to.be.true;
151+
});
152+
143153
describe('addCustomClass', () => {
144154
const type = { name: 'TypeTest' };
145155
const provider = { provide: type, useClass: type, name: 'test' };
@@ -229,6 +239,36 @@ describe('Module', () => {
229239
});
230240
});
231241

242+
describe('addCustomUseExisting', () => {
243+
const type = { name: 'TypeTest' };
244+
const provider = { provide: type, useExisting: type, name: 'test' };
245+
246+
let setSpy;
247+
beforeEach(() => {
248+
const collection = new Map();
249+
setSpy = sinon.spy(collection, 'set');
250+
(module as any)._providers = collection;
251+
});
252+
it('should store provider', () => {
253+
module.addCustomUseExisting(provider as any, (module as any)._providers);
254+
const factoryFn = (module as any)._providers.get(provider.name).metatype;
255+
expect(
256+
setSpy.calledWith(
257+
provider.name,
258+
new InstanceWrapper({
259+
host: module,
260+
name: provider.name,
261+
metatype: factoryFn,
262+
instance: null,
263+
inject: [provider.useExisting as any],
264+
isResolved: false,
265+
}),
266+
),
267+
).to.be.true;
268+
expect(factoryFn(provider.useExisting)).to.be.eql(type);
269+
});
270+
});
271+
232272
describe('when get instance', () => {
233273
describe('when metatype does not exists in providers collection', () => {
234274
beforeEach(() => {

0 commit comments

Comments
 (0)