Skip to content

Commit

Permalink
Prepare #540 - @TypedException
Browse files Browse the repository at this point in the history
  • Loading branch information
samchon committed Aug 15, 2023
1 parent a11e2db commit dc323df
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 15 deletions.
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/core",
"version": "1.6.3",
"version": "1.6.4-dev.20230816",
"description": "Super-fast validation decorators of NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -44,7 +44,7 @@
"raw-body": ">= 2.0.0",
"reflect-metadata": ">= 0.1.12",
"rxjs": ">= 6.0.0",
"typia": "^4.2.1"
"typia": "^4.2.3"
},
"peerDependencies": {
"@nestia/fetcher": ">= 1.6.3",
Expand All @@ -56,7 +56,7 @@
"reflect-metadata": ">= 0.1.12",
"rxjs": ">= 6.0.0",
"typescript": ">= 4.8.0",
"typia": ">= 4.2.1"
"typia": ">= 4.2.3"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
Expand Down
47 changes: 47 additions & 0 deletions packages/core/src/decorators/TypedException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import "reflect-metadata";

export function TypedException(
status: number,
description?: string | undefined,
): never;

export function TypedException<T extends object>(
status: number,
description?: string | undefined,
): MethodDecorator;

/**
* @internal
*/
export function TypedException<T extends object>(
status: number,
description?: string | undefined,
type?: string | undefined,
): MethodDecorator {
return function TypedException(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<any>,
) {
Reflect.defineMetadata(
`swagger/TypedException`,
{
status,
description,
type,
},
(target as any)[propertyKey],
);
return descriptor;
};
}
export namespace TypedException {
export interface IProps {
status: number;
description?: string | undefined;
/**
* @internal
*/
type: string;
}
}
1 change: 1 addition & 0 deletions packages/core/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from "./decorators/EncryptedRoute";
export * from "./utils/ExceptionManager";
export * from "./decorators/PlainBody";
export * from "./decorators/TypedBody";
export * from "./decorators/TypedException";
export * from "./decorators/TypedHeaders";
export * from "./decorators/TypedParam";
export * from "./decorators/TypedRoute";
Expand Down
40 changes: 40 additions & 0 deletions packages/core/src/programmers/TypedExceptionProgrammer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import ts from "typescript";

import { INestiaTransformProject } from "../options/INestiaTransformProject";

export namespace TypedExceptionProgrammer {
export const generate =
({ checker }: INestiaTransformProject) =>
(expression: ts.CallExpression): ts.CallExpression => {
// CHECK GENERIC ARGUMENT EXISTENCE
if (!expression.typeArguments?.[0]) throw new Error(NOT_SPECIFIED);

// GET TYPE INFO
const node: ts.TypeNode = expression.typeArguments[0];
const type: ts.Type = checker.getTypeFromTypeNode(node);

if (type.isTypeParameter()) throw new Error(NO_GENERIC_ARGUMENT);

// CHECK DUPLICATED TRNASFORMATION
if (expression.arguments.length === 3) return expression;

// DO TRANSFORM
const name: string = node.getFullText().trim();
return ts.factory.updateCallExpression(
expression,
expression.expression,
expression.typeArguments,
[
expression.arguments[0],
expression.arguments[1] ??
ts.factory.createIdentifier("undefined"),
ts.factory.createStringLiteral(name),
],
);
};
}

const NOT_SPECIFIED =
"Error on @nestia.core.TypedException(): generic argument is not specified.";
const NO_GENERIC_ARGUMENT =
"Error on @nestia.core.TypedException(): non-specified generic argument.";
17 changes: 6 additions & 11 deletions packages/core/src/transformers/MethodTransformer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ts from "typescript";

import { INestiaTransformProject } from "../options/INestiaTransformProject";
import { MethodDecoratorTransformer } from "./MethodDecoratorTransformer";
import { TypedRouteTransformer } from "./TypedRouteTransformer";

export namespace MethodTransformer {
export const transform =
Expand All @@ -23,15 +23,14 @@ export namespace MethodTransformer {

if (escaped === undefined) return method;

const operator = (deco: ts.Decorator): ts.Decorator =>
TypedRouteTransformer.transform(project)(escaped)(deco);

if (ts.getDecorators !== undefined)
return ts.factory.updateMethodDeclaration(
method,
(method.modifiers || []).map((mod) =>
ts.isDecorator(mod)
? MethodDecoratorTransformer.transform(project)(
escaped,
)(mod)
: mod,
ts.isDecorator(mod) ? operator(mod) : mod,
),
method.asteriskToken,
method.name,
Expand All @@ -44,11 +43,7 @@ export namespace MethodTransformer {
// eslint-disable-next-line
return (ts.factory.updateMethodDeclaration as any)(
method,
decorators.map((deco) =>
MethodDecoratorTransformer.transform(project)(escaped)(
deco,
),
),
decorators.map(operator),
(method as any).modifiers,
method.asteriskToken,
method.name,
Expand Down
51 changes: 51 additions & 0 deletions packages/core/src/transformers/TypedExceptionTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import path from "path";
import ts from "typescript";

import { INestiaTransformProject } from "../options/INestiaTransformProject";
import { TypedExceptionProgrammer } from "../programmers/TypedExceptionProgrammer";

export namespace TypedExceptionTransformer {
export const transform =
(project: INestiaTransformProject) =>
(decorator: ts.Decorator): ts.Decorator => {
if (!ts.isCallExpression(decorator.expression)) return decorator;

// CHECK SIGNATURE
const signature: ts.Signature | undefined =
project.checker.getResolvedSignature(decorator.expression);
if (!signature || !signature.declaration) return decorator;

// CHECK TO BE TRANSFORMED
const done: boolean = (() => {
// CHECK FILENAME
const location: string = path.resolve(
signature.declaration.getSourceFile().fileName,
);
if (location.indexOf(LIB_PATH) === -1 && location !== SRC_PATH)
return false;

// CHECK DUPLICATED
return decorator.expression.arguments.length !== 3;
})();
if (done === false) return decorator;

// DO TRANSFORM
return ts.factory.createDecorator(
TypedExceptionProgrammer.generate(project)(
decorator.expression,
),
);
};

const LIB_PATH = path.join(
"node_modules",
"@nestia",
"core",
"lib",
"decorators",
`TypedException.d.ts`,
);
const SRC_PATH = path.resolve(
path.join(__dirname, "..", "decorators", `TypedException.ts`),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ts from "typescript";
import { INestiaTransformProject } from "../options/INestiaTransformProject";
import { TypedRouteProgrammer } from "../programmers/TypedRouteProgrammer";

export namespace MethodDecoratorTransformer {
export namespace TypedRouteTransformer {
export const transform =
(project: INestiaTransformProject) =>
(type: ts.Type) =>
Expand Down

0 comments on commit dc323df

Please sign in to comment.