Skip to content

Commit 2e992d3

Browse files
tests() fix unit tests, update enhancers
1 parent cb062be commit 2e992d3

27 files changed

Lines changed: 329 additions & 251 deletions

packages/core/injector/container.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DynamicModule } from '@nestjs/common';
22
import { GLOBAL_MODULE_METADATA } from '@nestjs/common/constants';
3+
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
34
import { Type } from '@nestjs/common/interfaces/type.interface';
45
import { ApplicationConfig } from '../application-config';
56
import { CircularDependencyException } from '../errors/exceptions/circular-dependency.exception';
@@ -101,6 +102,10 @@ export class NestContainer {
101102
return this.modules;
102103
}
103104

105+
public getModuleByKey(moduleKey: string): Module {
106+
return this.modulesContainer.get(moduleKey);
107+
}
108+
104109
public async addImport(
105110
relatedModule: Type<any> | DynamicModule,
106111
token: string,
@@ -130,12 +135,16 @@ export class NestContainer {
130135
return module.addProvider(provider);
131136
}
132137

133-
public addInjectable(injectable: Type<any>, token: string) {
138+
public addInjectable(
139+
injectable: Type<any>,
140+
token: string,
141+
host: Type<Injectable>,
142+
) {
134143
if (!this.modules.has(token)) {
135144
throw new UnknownModuleException();
136145
}
137146
const module = this.modules.get(token);
138-
module.addInjectable(injectable);
147+
module.addInjectable(injectable, host);
139148
}
140149

141150
public addExportedProvider(provider: Type<any>, token: string) {

packages/core/injector/injector.ts

Lines changed: 34 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Scope } from '@nestjs/common';
21
import {
32
OPTIONAL_DEPS_METADATA,
43
OPTIONAL_PROPERTY_DEPS_METADATA,
@@ -18,15 +17,13 @@ import {
1817
import { RuntimeException } from '../errors/exceptions/runtime.exception';
1918
import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception';
2019
import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception';
21-
import { AsyncContext } from './../hooks';
2220
import { STATIC_CONTEXT } from './constants';
2321
import {
2422
ContextId,
2523
InstancePerContext,
2624
InstanceWrapper,
2725
} from './instance-wrapper';
2826
import { Module } from './module';
29-
import { ModulesContainer } from './modules-container';
3027

3128
/**
3229
* The type of an injectable dependency
@@ -75,17 +72,14 @@ export class Injector {
7572
contextId = STATIC_CONTEXT,
7673
) {
7774
const { metatype } = wrapper;
78-
const targetMetatype = collection.get(metatype.name);
79-
if (targetMetatype.instance !== null) {
75+
const targetWrapper = collection.get(metatype.name);
76+
if (targetWrapper.instance !== null) {
8077
return;
8178
}
8279
const loadInstance = (instances: any[]) => {
83-
const instanceWrapper = new InstanceWrapper({
84-
instance: new metatype(...instances),
85-
metatype,
86-
host: module,
87-
});
88-
collection.set(metatype.name, instanceWrapper);
80+
targetWrapper.instance = targetWrapper.isDependencyTreeStatic()
81+
? new metatype(...instances)
82+
: Object.create(metatype);
8983
};
9084
await this.resolveConstructorParams(
9185
wrapper,
@@ -199,7 +193,6 @@ export class Injector {
199193
instances,
200194
wrapper,
201195
targetWrapper,
202-
module.id,
203196
contextId,
204197
);
205198
this.applyProperties(instance, properties);
@@ -526,89 +519,55 @@ export class Injector {
526519
instances: any[],
527520
wrapper: InstanceWrapper,
528521
targetMetatype: InstanceWrapper,
529-
moduleId: string,
530522
contextId = STATIC_CONTEXT,
531523
): Promise<T> {
532-
const { metatype, inject, name, scope } = wrapper;
524+
const { metatype, inject } = wrapper;
533525
const instanceHost = targetMetatype.getInstanceByContextId(contextId);
534-
const instanceKey = moduleId + name;
526+
const isDependencyTreeStatic = wrapper.isDependencyTreeStatic();
527+
const isInContext =
528+
(isDependencyTreeStatic && contextId === STATIC_CONTEXT) ||
529+
(!isDependencyTreeStatic && contextId !== STATIC_CONTEXT);
535530

536-
if (isNil(inject)) {
531+
if (isNil(inject) && isInContext) {
537532
const targetInstance = wrapper.getInstanceByContextId(contextId);
533+
538534
targetInstance.instance = wrapper.forwardRef
539535
? Object.assign(targetInstance.instance, new metatype(...instances))
540536
: new metatype(...instances);
541-
542-
if (scope === Scope.LAZY_ASYNC) {
543-
this.mergeAsyncProxy(
544-
instances,
545-
targetInstance,
546-
instanceKey,
547-
metatype,
548-
(...args: any[]) => new metatype(...args),
549-
);
550-
}
551-
} else {
537+
} else if (isInContext) {
552538
const factoryReturnValue = ((targetMetatype.metatype as any) as Function)(
553539
...instances,
554540
);
555541
instanceHost.instance = await factoryReturnValue;
556-
if (scope === Scope.LAZY_ASYNC) {
557-
this.mergeAsyncProxy(
558-
instances,
559-
instanceHost,
560-
instanceKey,
561-
metatype,
562-
(...args: any[]) =>
563-
((targetMetatype.metatype as any) as Function)(...args),
564-
);
565-
}
566542
}
567543
instanceHost.isResolved = true;
568544
return instanceHost.instance;
569545
}
570546

571-
mergeAsyncProxy(
572-
instances: any[],
573-
targetInstance: InstancePerContext<any>,
574-
instanceKey: string,
575-
metatype: Type<any>,
576-
factory: (...intances: any[]) => any,
577-
) {
578-
const asyncContext = AsyncContext.getInstance();
579-
asyncContext.set(instanceKey, targetInstance.instance);
580-
581-
const proxy = (target: unknown, property: string | number | symbol) => {
582-
if (!(property in (target as object))) {
583-
return;
584-
}
585-
const cachedInstance = asyncContext.get(instanceKey);
586-
if (cachedInstance) {
587-
return cachedInstance[property];
588-
}
589-
const value = factory(...instances);
590-
asyncContext.set(instanceKey, value);
591-
return value[property];
592-
};
593-
targetInstance.instance = new Proxy(targetInstance.instance, {
594-
get: proxy,
595-
set: proxy,
596-
});
597-
}
598-
599-
async loadControllerPerContext<T>(
600-
instance: Controller,
601-
modulesContainer: ModulesContainer,
602-
moduleKey: string,
547+
async loadPerContext<T = any>(
548+
instance: T,
549+
module: Module,
550+
collection: Map<string, InstanceWrapper>,
603551
ctx: ContextId,
604552
): Promise<T> {
605-
const module = modulesContainer.get(moduleKey);
553+
const wrapper = collection.get(
554+
instance.constructor && instance.constructor.name,
555+
);
556+
await this.loadInstance(wrapper, collection, module, ctx);
557+
await this.loadEnhancersPerContext(wrapper, module, ctx);
606558

607-
const { controllers } = module;
608-
const controller = controllers.get(instance.constructor.name);
609-
await this.loadInstance(controller, controllers, module, ctx);
559+
const host = wrapper.getInstanceByContextId(ctx);
560+
return host && (host.instance as T);
561+
}
610562

611-
const wrapper = controller.getInstanceByContextId(ctx);
612-
return wrapper && (wrapper.instance as T);
563+
async loadEnhancersPerContext(
564+
wrapper: InstanceWrapper,
565+
module: Module,
566+
ctx: ContextId,
567+
) {
568+
const enhancers = wrapper.getEnhancersMetadata();
569+
const loadEnhancer = (item: InstanceWrapper) =>
570+
this.loadInstance(item, module.injectables, module, ctx);
571+
await Promise.all(enhancers.map(loadEnhancer));
613572
}
614573
}

packages/core/injector/instance-wrapper.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ export interface PropertyMetadata {
1919
wrapper: InstanceWrapper;
2020
}
2121

22-
export interface InstanceMetadataStore {
22+
interface InstanceMetadataStore {
2323
dependencies?: InstanceWrapper[];
2424
properties?: PropertyMetadata[];
25+
enhancers?: InstanceWrapper[];
2526
}
2627

2728
export class InstanceWrapper<T = any> {
@@ -91,19 +92,36 @@ export class InstanceWrapper<T = any> {
9192
return this[INSTANCE_METADATA_SYMBOL].properties;
9293
}
9394

95+
addEnhancerMetadata(wrapper: InstanceWrapper) {
96+
if (!this[INSTANCE_METADATA_SYMBOL].enhancers) {
97+
this[INSTANCE_METADATA_SYMBOL].enhancers = [];
98+
}
99+
this[INSTANCE_METADATA_SYMBOL].enhancers.push(wrapper);
100+
}
101+
102+
getEnhancersMetadata(): InstanceWrapper[] {
103+
return this[INSTANCE_METADATA_SYMBOL].enhancers;
104+
}
105+
94106
isDependencyTreeStatic(): boolean {
95107
if (this.scope === Scope.REQUEST) {
96108
return false;
97109
}
98-
const { dependencies, properties } = this[INSTANCE_METADATA_SYMBOL];
99-
const isStatic =
110+
const { dependencies, properties, enhancers } = this[
111+
INSTANCE_METADATA_SYMBOL
112+
];
113+
let isStatic =
100114
(dependencies && this.isWrapperStatic(dependencies)) || !dependencies;
101115

102116
if (!properties || !isStatic) {
103117
return isStatic;
104118
}
105-
const propHosts = properties.map(item => item.wrapper);
106-
return isStatic && this.isWrapperStatic(propHosts);
119+
const propertiesHosts = properties.map(item => item.wrapper);
120+
isStatic = isStatic && this.isWrapperStatic(propertiesHosts);
121+
if (!enhancers || !isStatic) {
122+
return isStatic;
123+
}
124+
return this.isWrapperStatic(enhancers);
107125
}
108126

109127
private isWrapperStatic(tree: InstanceWrapper[]) {

packages/core/injector/module.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,27 @@ export class Module {
233233
);
234234
}
235235

236-
public addInjectable<T = Injectable>(injectable: Type<T>) {
236+
public addInjectable<T extends Injectable>(
237+
injectable: Type<T>,
238+
host?: Type<T>,
239+
) {
237240
if (this.isCustomProvider(injectable)) {
238241
return this.addCustomProvider(injectable, this._injectables);
239242
}
240-
this._injectables.set(
241-
injectable.name,
242-
new InstanceWrapper({
243-
name: injectable.name,
244-
metatype: injectable,
245-
instance: null,
246-
isResolved: false,
247-
scope: this.getClassScope(injectable),
248-
host: this,
249-
}),
250-
);
243+
const instanceWrapper = new InstanceWrapper({
244+
name: injectable.name,
245+
metatype: injectable,
246+
instance: null,
247+
isResolved: false,
248+
scope: this.getClassScope(injectable),
249+
host: this,
250+
});
251+
this._injectables.set(injectable.name, instanceWrapper);
252+
253+
if (host) {
254+
const hostWrapper = this._controllers.get(host && host.name);
255+
hostWrapper && hostWrapper.addEnhancerMetadata(instanceWrapper);
256+
}
251257
}
252258

253259
public addProvider(provider: ProviderMetatype): string {
Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Scope, Type } from '@nestjs/common';
2+
import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants';
13
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
24
import { InstanceWrapper } from './../injector/instance-wrapper';
35

@@ -8,46 +10,51 @@ export class MiddlewareContainer {
810
Set<MiddlewareConfiguration>
911
>();
1012

11-
public getMiddleware(module: string): Map<string, InstanceWrapper> {
13+
public getMiddlewareCollection(module: string): Map<string, InstanceWrapper> {
1214
return this.middleware.get(module) || new Map();
1315
}
1416

15-
public getConfigs(): Map<string, Set<MiddlewareConfiguration>> {
17+
public getConfigurations(): Map<string, Set<MiddlewareConfiguration>> {
1618
return this.configurationSets;
1719
}
1820

19-
public addConfig(configList: MiddlewareConfiguration[], module: string) {
20-
const middleware = this.getCurrentMiddleware(module);
21-
const targetConfig = this.getCurrentConfig(module);
21+
public insertConfig(configList: MiddlewareConfiguration[], module: string) {
22+
const middleware = this.getTargetMiddleware(module);
23+
const targetConfig = this.getTargetConfig(module);
2224

2325
const configurations = configList || [];
26+
const insertMiddleware = <T extends Type<any>>(metatype: T) => {
27+
const token = metatype.name;
28+
middleware.set(
29+
token,
30+
new InstanceWrapper({
31+
scope: this.getClassScope(metatype),
32+
metatype,
33+
}),
34+
);
35+
};
2436
configurations.forEach(config => {
25-
const callback = metatype => {
26-
const token = metatype.name;
27-
middleware.set(
28-
token,
29-
new InstanceWrapper({
30-
instance: null,
31-
metatype,
32-
}),
33-
);
34-
};
35-
[].concat(config.middleware).map(callback);
37+
[].concat(config.middleware).map(insertMiddleware);
3638
targetConfig.add(config);
3739
});
3840
}
3941

40-
private getCurrentMiddleware(module: string) {
42+
private getTargetMiddleware(module: string) {
4143
if (!this.middleware.has(module)) {
4244
this.middleware.set(module, new Map<string, InstanceWrapper>());
4345
}
4446
return this.middleware.get(module);
4547
}
4648

47-
private getCurrentConfig(module: string) {
49+
private getTargetConfig(module: string) {
4850
if (!this.configurationSets.has(module)) {
4951
this.configurationSets.set(module, new Set<MiddlewareConfiguration>());
5052
}
5153
return this.configurationSets.get(module);
5254
}
55+
56+
private getClassScope<T = any>(type: Type<T>): Scope {
57+
const metadata = Reflect.getMetadata(SCOPE_OPTIONS_METADATA, type);
58+
return metadata && metadata.scope;
59+
}
5360
}

0 commit comments

Comments
 (0)