mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
feature(core) instantiate class dynamically (ModuleRef)
This commit is contained in:
@@ -1,65 +1,61 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
|
||||
import { InstanceWrapper, NestContainer } from './container';
|
||||
import { NestContainer } from './container';
|
||||
import { ContainerScanner } from './container-scanner';
|
||||
import { Injector } from './injector';
|
||||
import { Module } from './module';
|
||||
|
||||
export abstract class ModuleRef {
|
||||
private flattenModuleFixture: Partial<Module>;
|
||||
private readonly injector = new Injector();
|
||||
private readonly containerScanner: ContainerScanner;
|
||||
|
||||
constructor(protected readonly container: NestContainer) {}
|
||||
constructor(protected readonly container: NestContainer) {
|
||||
this.containerScanner = new ContainerScanner(container);
|
||||
}
|
||||
|
||||
public abstract get<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
options?: { strict: boolean },
|
||||
): TResult;
|
||||
|
||||
public abstract create<T = any>(type: Type<T>): Promise<T>;
|
||||
|
||||
protected find<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
): TResult {
|
||||
this.initFlattenModule();
|
||||
return this.findInstanceByPrototypeOrToken<TInput, TResult>(
|
||||
typeOrToken,
|
||||
this.flattenModuleFixture,
|
||||
);
|
||||
return this.containerScanner.find<TInput, TResult>(typeOrToken);
|
||||
}
|
||||
|
||||
protected async instantiateClass<T = any>(
|
||||
type: Type<T>,
|
||||
module: Module,
|
||||
): Promise<T> {
|
||||
const wrapper = {
|
||||
name: type.name,
|
||||
metatype: type,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
};
|
||||
return new Promise<T>(async (resolve, reject) => {
|
||||
try {
|
||||
await this.injector.resolveConstructorParams<T>(
|
||||
wrapper,
|
||||
module,
|
||||
undefined,
|
||||
async instances => resolve(new type(...instances)),
|
||||
);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected findInstanceByPrototypeOrToken<TInput = any, TResult = TInput>(
|
||||
metatypeOrToken: Type<TInput> | string | symbol,
|
||||
contextModule: Partial<Module>,
|
||||
): TResult {
|
||||
const dependencies = new Map([
|
||||
...contextModule.components,
|
||||
...contextModule.routes,
|
||||
...contextModule.injectables,
|
||||
]);
|
||||
const name = isFunction(metatypeOrToken)
|
||||
? (metatypeOrToken as any).name
|
||||
: metatypeOrToken;
|
||||
const instanceWrapper = dependencies.get(name);
|
||||
if (!instanceWrapper) {
|
||||
throw new UnknownElementException();
|
||||
}
|
||||
return (instanceWrapper as InstanceWrapper<any>).instance;
|
||||
}
|
||||
|
||||
private initFlattenModule() {
|
||||
if (this.flattenModuleFixture) {
|
||||
return void 0;
|
||||
}
|
||||
const modules = this.container.getModules();
|
||||
const initialValue = {
|
||||
components: [],
|
||||
routes: [],
|
||||
injectables: [],
|
||||
};
|
||||
this.flattenModuleFixture = [...modules.values()].reduce(
|
||||
(flatten, curr) => ({
|
||||
components: [...flatten.components, ...curr.components],
|
||||
routes: [...flatten.routes, ...curr.routes],
|
||||
injectables: [...flatten.injectables, ...curr.injectables],
|
||||
}),
|
||||
initialValue,
|
||||
) as any;
|
||||
return this.containerScanner.findInstanceByPrototypeOrToken<
|
||||
TInput,
|
||||
TResult
|
||||
>(metatypeOrToken, contextModule);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user