mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
feature(@nestjs/core) add @Optional() decorator #847
This commit is contained in:
@@ -12,6 +12,7 @@ export const GLOBAL_MODULE_METADATA = '__globalModule__';
|
||||
export const PATH_METADATA = 'path';
|
||||
export const PARAMTYPES_METADATA = 'design:paramtypes';
|
||||
export const SELF_DECLARED_DEPS_METADATA = 'self:paramtypes';
|
||||
export const OPTIONAL_DEPS_METADATA = 'optional:paramtypes';
|
||||
export const METHOD_METADATA = 'method';
|
||||
export const ROUTE_ARGS_METADATA = '__routeArguments__';
|
||||
export const CUSTOM_ROUTE_AGRS_METADATA = '__customRouteArgs__';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
export * from './controller.decorator';
|
||||
export * from './component.decorator';
|
||||
export * from './dependencies.decorator';
|
||||
export * from './inject.decorator';
|
||||
export * from './catch.decorator';
|
||||
export * from './exception-filters.decorator';
|
||||
export * from './use-pipes.decorator';
|
||||
export * from './use-guards.decorator';
|
||||
export * from './reflect-metadata.decorator';
|
||||
export * from './use-interceptors.decorator';
|
||||
export * from './bind.decorator';
|
||||
export * from './catch.decorator';
|
||||
export * from './component.decorator';
|
||||
export * from './controller.decorator';
|
||||
export * from './dependencies.decorator';
|
||||
export * from './exception-filters.decorator';
|
||||
export * from './inject.decorator';
|
||||
export * from './optional.decorator';
|
||||
export * from './reflect-metadata.decorator';
|
||||
export * from './use-guards.decorator';
|
||||
export * from './use-interceptors.decorator';
|
||||
export * from './use-pipes.decorator';
|
||||
|
||||
12
packages/common/decorators/core/optional.decorator.ts
Normal file
12
packages/common/decorators/core/optional.decorator.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'reflect-metadata';
|
||||
import { OPTIONAL_DEPS_METADATA } from '../../constants';
|
||||
|
||||
/**
|
||||
* Sets dependency as an optional one.
|
||||
*/
|
||||
export function Optional(): ParameterDecorator {
|
||||
return (target, key, index) => {
|
||||
const args = Reflect.getMetadata(OPTIONAL_DEPS_METADATA, target) || [];
|
||||
Reflect.defineMetadata(OPTIONAL_DEPS_METADATA, [...args, index], target);
|
||||
};
|
||||
}
|
||||
@@ -7,39 +7,39 @@
|
||||
|
||||
export * from './decorators';
|
||||
export * from './enums';
|
||||
export {
|
||||
NestModule,
|
||||
INestApplication,
|
||||
INestMicroservice,
|
||||
NestMiddleware,
|
||||
MiddlewareFunction,
|
||||
MiddlewareConsumer,
|
||||
OnModuleInit,
|
||||
ExceptionFilter,
|
||||
WebSocketAdapter,
|
||||
PipeTransform,
|
||||
Paramtype,
|
||||
ArgumentMetadata,
|
||||
OnModuleDestroy,
|
||||
ExecutionContext,
|
||||
CanActivate,
|
||||
RpcExceptionFilter,
|
||||
WsExceptionFilter,
|
||||
NestInterceptor,
|
||||
DynamicModule,
|
||||
INestApplicationContext,
|
||||
HttpServer,
|
||||
Provider,
|
||||
Type,
|
||||
HttpServerFactory,
|
||||
ArgumentsHost,
|
||||
INestExpressApplication,
|
||||
INestFastifyApplication,
|
||||
ForwardReference,
|
||||
} from './interfaces';
|
||||
export * from './interceptors';
|
||||
export * from './services/logger.service';
|
||||
export * from './pipes';
|
||||
export * from './utils';
|
||||
export * from './exceptions';
|
||||
export * from './http';
|
||||
export * from './interceptors';
|
||||
export {
|
||||
ArgumentMetadata,
|
||||
ArgumentsHost,
|
||||
CanActivate,
|
||||
DynamicModule,
|
||||
ExceptionFilter,
|
||||
ExecutionContext,
|
||||
ForwardReference,
|
||||
HttpServer,
|
||||
HttpServerFactory,
|
||||
INestApplication,
|
||||
INestApplicationContext,
|
||||
INestExpressApplication,
|
||||
INestFastifyApplication,
|
||||
INestMicroservice,
|
||||
MiddlewareConsumer,
|
||||
MiddlewareFunction,
|
||||
NestInterceptor,
|
||||
NestMiddleware,
|
||||
NestModule,
|
||||
OnModuleDestroy,
|
||||
OnModuleInit,
|
||||
Paramtype,
|
||||
PipeTransform,
|
||||
Provider,
|
||||
RpcExceptionFilter,
|
||||
Type,
|
||||
WebSocketAdapter,
|
||||
WsExceptionFilter,
|
||||
} from './interfaces';
|
||||
export * from './pipes';
|
||||
export * from './services/logger.service';
|
||||
export * from './utils';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Optional } from '../decorators';
|
||||
import { ArgumentMetadata, BadRequestException } from '../index';
|
||||
import { ValidatorOptions } from '../interfaces/external/validator-options.interface';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
@@ -19,7 +20,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
protected isDetailedOutputDisabled: boolean;
|
||||
protected validatorOptions: ValidatorOptions;
|
||||
|
||||
constructor(options?: ValidationPipeOptions) {
|
||||
constructor(@Optional() options?: ValidationPipeOptions) {
|
||||
options = options || {};
|
||||
const { transform, disableErrorMessages, ...validatorOptions } = options;
|
||||
this.isTransformEnabled = !!transform;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PARAMTYPES_METADATA, SELF_DECLARED_DEPS_METADATA } from '@nestjs/common/constants';
|
||||
import { OPTIONAL_DEPS_METADATA, PARAMTYPES_METADATA, SELF_DECLARED_DEPS_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface';
|
||||
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
@@ -99,7 +99,7 @@ export class Injector {
|
||||
await this.loadInstance<Injectable>(wrapper, components, module);
|
||||
}
|
||||
|
||||
public applyDoneSubject<T>(wrapper: InstanceWrapper<T>): () => void {
|
||||
public applyDoneHook<T>(wrapper: InstanceWrapper<T>): () => void {
|
||||
let done: () => void;
|
||||
wrapper.done$ = new Promise<void>((resolve, reject) => {
|
||||
done = resolve;
|
||||
@@ -116,13 +116,15 @@ export class Injector {
|
||||
if (wrapper.isPending) {
|
||||
return await wrapper.done$;
|
||||
}
|
||||
const done = this.applyDoneSubject(wrapper);
|
||||
const done = this.applyDoneHook(wrapper);
|
||||
const { metatype, name, inject } = wrapper;
|
||||
const currentMetatype = collection.get(name);
|
||||
if (isUndefined(currentMetatype)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (currentMetatype.isResolved) return null;
|
||||
if (currentMetatype.isResolved) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
await this.resolveConstructorParams<T>(
|
||||
wrapper,
|
||||
@@ -153,22 +155,34 @@ export class Injector {
|
||||
callback: (args) => void,
|
||||
) {
|
||||
let isResolved = true;
|
||||
|
||||
const dependencies = isNil(inject)
|
||||
? this.reflectConstructorParams(wrapper.metatype)
|
||||
: inject;
|
||||
const optionalDependenciesIds = isNil(inject)
|
||||
? this.reflectOptionalParams(wrapper.metatype)
|
||||
: [];
|
||||
|
||||
const instances = await Promise.all(
|
||||
dependencies.map(async (param, index) => {
|
||||
const paramWrapper = await this.resolveSingleParam<T>(
|
||||
wrapper,
|
||||
param,
|
||||
{ index, dependencies },
|
||||
module,
|
||||
);
|
||||
if (!paramWrapper.isResolved && !paramWrapper.forwardRef) {
|
||||
isResolved = false;
|
||||
try {
|
||||
const paramWrapper = await this.resolveSingleParam<T>(
|
||||
wrapper,
|
||||
param,
|
||||
{ index, dependencies },
|
||||
module,
|
||||
);
|
||||
if (!paramWrapper.isResolved && !paramWrapper.forwardRef) {
|
||||
isResolved = false;
|
||||
}
|
||||
return paramWrapper.instance;
|
||||
} catch (err) {
|
||||
const isOptional = optionalDependenciesIds.includes(index);
|
||||
if (!isOptional) {
|
||||
throw err;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return paramWrapper.instance;
|
||||
}),
|
||||
);
|
||||
isResolved && (await callback(instances));
|
||||
@@ -182,6 +196,10 @@ export class Injector {
|
||||
return paramtypes;
|
||||
}
|
||||
|
||||
public reflectOptionalParams<T>(type: Type<T>): any[] {
|
||||
return Reflect.getMetadata(OPTIONAL_DEPS_METADATA, type) || [];
|
||||
}
|
||||
|
||||
public reflectSelfParams<T>(type: Type<T>): any[] {
|
||||
return Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, type) || [];
|
||||
}
|
||||
@@ -313,4 +331,4 @@ export class Injector {
|
||||
};
|
||||
return modules.concat.apply(modules, modules.map(flatten));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('Injector', () => {
|
||||
expect(result).to.be.eql(value);
|
||||
});
|
||||
|
||||
it('should return null when metatype is resolved', async () => {
|
||||
it('should return undefined when metatype is resolved', async () => {
|
||||
const value = 'test';
|
||||
const result = await injector.loadInstance(
|
||||
{
|
||||
@@ -113,7 +113,7 @@ describe('Injector', () => {
|
||||
moduleDeps.components,
|
||||
moduleDeps,
|
||||
);
|
||||
expect(result).to.be.null;
|
||||
expect(result).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
"outDir": "dist",
|
||||
"lib": [
|
||||
"es7"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"packages/**/*",
|
||||
|
||||
Reference in New Issue
Block a user