Skip to content

Commit dc33ea5

Browse files
Merge pull request nestjs#903 from nestjs/feature/module-ref
feature(@nestjs/core) expose container to ModuleRef, add close()
2 parents 780bd9c + 1527e12 commit dc33ea5

22 files changed

Lines changed: 241 additions & 314 deletions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
"packages/microservices/exceptions/",
138138
"packages/microservices/microservices-module.ts",
139139
"packages/core/middleware/middleware-module.ts",
140+
"packages/core/injector/module-ref.ts",
140141
"packages/common/services/logger.service.ts"
141142
],
142143
"extension": [
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Type } from './type.interface';
2-
import { LoggerService } from '../services/logger.service';
32

43
export interface INestApplicationContext {
54
/**
@@ -10,10 +9,17 @@ export interface INestApplicationContext {
109

1110
/**
1211
* Retrieves an instance of either injectable or controller available anywhere, otherwise, throws exception.
13-
* @returns {T}
12+
* @returns {TResult}
1413
*/
15-
get<T>(
16-
typeOrToken: Type<T> | string | symbol,
14+
get<TInput = any, TResult = TInput>(
15+
typeOrToken: Type<TInput> | string | symbol,
1716
options?: { strict: boolean },
18-
): T;
17+
): TResult;
18+
19+
/**
20+
* Terminates the application
21+
*
22+
* @returns {Promise<void>}
23+
*/
24+
close(): Promise<void>;
1925
}

packages/core/exceptions/base-exception-filter-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { FILTER_CATCH_EXCEPTIONS } from '@nestjs/common/constants';
2+
import { Type } from '@nestjs/common/interfaces';
23
import { ExceptionFilter } from '@nestjs/common/interfaces/exceptions/exception-filter.interface';
3-
import { Type } from '@nestjs/common/interfaces/index';
44
import { isEmpty, isFunction, isUndefined } from '@nestjs/common/utils/shared.utils';
55
import iterate from 'iterare';
66
import 'reflect-metadata';
7+
import { ContextCreator } from '../helpers/context-creator';
78
import { NestContainer } from '../injector/container';
8-
import { ContextCreator } from './../helpers/context-creator';
99

1010
export class BaseExceptionFilterContext extends ContextCreator {
1111
protected moduleContext: string;

packages/core/guards/guards-context-creator.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
import 'reflect-metadata';
2-
import iterate from 'iterare';
3-
import { Controller } from '@nestjs/common/interfaces';
4-
import { GUARDS_METADATA } from '@nestjs/common/constants';
5-
import {
6-
isUndefined,
7-
isFunction,
8-
isNil,
9-
isEmpty,
10-
} from '@nestjs/common/utils/shared.utils';
11-
import { ContextCreator } from './../helpers/context-creator';
12-
import { NestContainer } from '../injector/container';
131
import { CanActivate } from '@nestjs/common';
2+
import { GUARDS_METADATA } from '@nestjs/common/constants';
3+
import { Controller } from '@nestjs/common/interfaces';
144
import { ConfigurationProvider } from '@nestjs/common/interfaces/configuration-provider.interface';
5+
import { isEmpty, isFunction, isUndefined } from '@nestjs/common/utils/shared.utils';
6+
import iterate from 'iterare';
7+
import 'reflect-metadata';
8+
import { ContextCreator } from '../helpers/context-creator';
9+
import { NestContainer } from '../injector/container';
1510

1611
export class GuardsContextCreator extends ContextCreator {
1712
private moduleContext: string;

packages/core/helpers/context-creator.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import 'reflect-metadata';
2-
import iterate from 'iterare';
31
import { Controller } from '@nestjs/common/interfaces';
4-
import { isUndefined, isFunction } from '@nestjs/common/utils/shared.utils';
5-
import { ApplicationConfig } from './../application-config';
2+
import 'reflect-metadata';
63

74
export abstract class ContextCreator {
85
public abstract createConcreteContext<T extends any[], R extends any[]>(

packages/core/helpers/external-context-creator.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import 'reflect-metadata';
2-
import { GuardsContextCreator } from './../guards/guards-context-creator';
3-
import { GuardsConsumer } from './../guards/guards-consumer';
4-
import { InterceptorsContextCreator } from './../interceptors/interceptors-context-creator';
5-
import { InterceptorsConsumer } from './../interceptors/interceptors-consumer';
1+
import { ForbiddenException } from '@nestjs/common';
62
import { Controller } from '@nestjs/common/interfaces';
3+
import 'reflect-metadata';
74
import { FORBIDDEN_MESSAGE } from '../guards/constants';
8-
import { ForbiddenException } from '@nestjs/common';
9-
import { Module } from './../injector/module';
10-
import { ModulesContainer } from './../injector/modules-container';
5+
import { GuardsConsumer } from '../guards/guards-consumer';
6+
import { GuardsContextCreator } from '../guards/guards-context-creator';
7+
import { Module } from '../injector/module';
8+
import { ModulesContainer } from '../injector/modules-container';
9+
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
10+
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
1111

1212
export class ExternalContextCreator {
1313
constructor(

packages/core/injector/container.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { DynamicModule } from '@nestjs/common';
22
import { GLOBAL_MODULE_METADATA } from '@nestjs/common/constants';
33
import { Type } from '@nestjs/common/interfaces/type.interface';
44
import 'reflect-metadata';
5+
import { ApplicationConfig } from '../application-config';
6+
import { InvalidModuleException } from '../errors/exceptions/invalid-module.exception';
57
import { UnknownModuleException } from '../errors/exceptions/unknown-module.exception';
6-
import { ApplicationConfig } from './../application-config';
7-
import { InvalidModuleException } from './../errors/exceptions/invalid-module.exception';
88
import { ModuleCompiler } from './compiler';
99
import { Module } from './module';
1010
import { ModulesContainer } from './modules-container';

packages/core/injector/injector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { Type } from '@nestjs/common/interfaces/type.interface';
55
import { isFunction, isNil, isUndefined } from '@nestjs/common/utils/shared.utils';
66
import 'reflect-metadata';
77
import { RuntimeException } from '../errors/exceptions/runtime.exception';
8+
import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception';
89
import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception';
910
import { MiddlewareWrapper } from '../middleware/container';
10-
import { UndefinedDependencyException } from './../errors/exceptions/undefined-dependency.exception';
1111
import { InstanceWrapper } from './container';
1212
import { Module } from './module';
1313

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,65 @@
1-
import { OpaqueToken } from './module';
1+
import { Type } from '@nestjs/common';
2+
import { isFunction } from '@nestjs/common/utils/shared.utils';
3+
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
4+
import { InstanceWrapper, NestContainer } from './container';
5+
import { Module } from './module';
26

37
export abstract class ModuleRef {
4-
public abstract get<T>(type: OpaqueToken): T;
8+
private flattenModuleFixture: Partial<Module>;
9+
10+
constructor(protected readonly container: NestContainer) {}
11+
12+
public abstract get<TInput = any, TResult = TInput>(
13+
typeOrToken: Type<TInput> | string | symbol,
14+
options?: { strict: boolean },
15+
): TResult;
16+
17+
protected find<TInput = any, TResult = TInput>(
18+
typeOrToken: Type<TInput> | string | symbol,
19+
): TResult {
20+
this.initFlattenModule();
21+
return this.findInstanceByPrototypeOrToken<TInput, TResult>(
22+
typeOrToken,
23+
this.flattenModuleFixture,
24+
);
25+
}
26+
27+
protected findInstanceByPrototypeOrToken<TInput = any, TResult = TInput>(
28+
metatypeOrToken: Type<TInput> | string | symbol,
29+
contextModule: Partial<Module>,
30+
): TResult {
31+
const dependencies = new Map([
32+
...contextModule.components,
33+
...contextModule.routes,
34+
...contextModule.injectables,
35+
]);
36+
const name = isFunction(metatypeOrToken)
37+
? (metatypeOrToken as any).name
38+
: metatypeOrToken;
39+
const instanceWrapper = dependencies.get(name);
40+
if (!instanceWrapper) {
41+
throw new UnknownElementException();
42+
}
43+
return (instanceWrapper as InstanceWrapper<any>).instance;
44+
}
45+
46+
private initFlattenModule() {
47+
if (this.flattenModuleFixture) {
48+
return void 0;
49+
}
50+
const modules = this.container.getModules();
51+
const initialValue = {
52+
components: [],
53+
routes: [],
54+
injectables: [],
55+
};
56+
this.flattenModuleFixture = [...modules.values()].reduce(
57+
(flatten, curr) => ({
58+
components: [...flatten.components, ...curr.components],
59+
routes: [...flatten.routes, ...curr.routes],
60+
injectables: [...flatten.injectables, ...curr.injectables],
61+
}),
62+
initialValue,
63+
) as any;
64+
}
565
}

packages/core/injector/module.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { Type } from '@nestjs/common/interfaces/type.interface';
33
import { isFunction, isNil, isString, isSymbol, isUndefined } from '@nestjs/common/utils/shared.utils';
44
import { RuntimeException } from '../errors/exceptions/runtime.exception';
55
import { UnknownExportException } from '../errors/exceptions/unknown-export.exception';
6+
import { GuardsConsumer } from '../guards/guards-consumer';
7+
import { GuardsContextCreator } from '../guards/guards-context-creator';
8+
import { ExternalContextCreator } from '../helpers/external-context-creator';
9+
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
10+
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
611
import { Reflector } from '../services/reflector.service';
7-
import { GuardsConsumer } from './../guards/guards-consumer';
8-
import { GuardsContextCreator } from './../guards/guards-context-creator';
9-
import { ExternalContextCreator } from './../helpers/external-context-creator';
10-
import { InterceptorsConsumer } from './../interceptors/interceptors-consumer';
11-
import { InterceptorsContextCreator } from './../interceptors/interceptors-context-creator';
1212
import { InstanceWrapper, NestContainer } from './container';
1313
import { ModuleRef } from './module-ref';
1414
import { ModulesContainer } from './modules-container';
@@ -41,7 +41,7 @@ export class Module {
4141
constructor(
4242
private readonly _metatype: Type<any>,
4343
private readonly _scope: Type<any>[],
44-
container: NestContainer,
44+
private readonly container: NestContainer,
4545
) {
4646
this.addCoreInjectables(container);
4747
}
@@ -92,7 +92,7 @@ export class Module {
9292
}
9393

9494
public addModuleRef() {
95-
const moduleRef = this.createModuleRefMetatype(this._components);
95+
const moduleRef = this.createModuleRefMetatype();
9696
this._components.set(ModuleRef.name, {
9797
name: ModuleRef.name,
9898
metatype: ModuleRef as any,
@@ -324,15 +324,24 @@ export class Module {
324324
});
325325
}
326326

327-
public createModuleRefMetatype(components) {
328-
return class {
329-
public readonly components = components;
330-
331-
public get<T>(type: OpaqueToken): T {
332-
const name = isFunction(type) ? (type as Type<any>).name : type;
333-
const exists = this.components.has(name);
327+
public createModuleRefMetatype(): any {
328+
const self = this;
329+
return class extends ModuleRef {
330+
constructor() {
331+
super(self.container);
332+
}
334333

335-
return exists ? (this.components.get(name).instance as T) : null;
334+
public get<TInput = any, TResult = TInput>(
335+
typeOrToken: Type<TInput> | string | symbol,
336+
options: { strict: boolean } = { strict: true },
337+
): TResult {
338+
if (!(options && options.strict)) {
339+
return this.find<TInput, TResult>(typeOrToken);
340+
}
341+
return this.findInstanceByPrototypeOrToken<TInput, TResult>(
342+
typeOrToken,
343+
self,
344+
);
336345
}
337346
};
338347
}

0 commit comments

Comments
 (0)