diff --git a/benchmark/package.json b/benchmark/package.json index 556891c30..283780072 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -25,7 +25,7 @@ "homepage": "https://nestia.io", "dependencies": { "@nestia/core": "^2.2.1-dev.20231012", - "typia": "^5.2.2" + "typia": "^5.2.3" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", diff --git a/packages/core/package.json b/packages/core/package.json index 138f3edce..0df7d7576 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/core", - "version": "2.3.0-dev.20231019", + "version": "2.3.0-dev.20231020", "description": "Super-fast validation decorators of NestJS", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -34,7 +34,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^2.3.0-dev.20231019", + "@nestia/fetcher": "^2.3.0-dev.20231020", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@nestjs/platform-express": ">=7.0.1", @@ -44,10 +44,10 @@ "raw-body": ">=2.0.0", "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.0", - "typia": "^5.2.2" + "typia": "^5.2.3" }, "peerDependencies": { - "@nestia/fetcher": ">=2.3.0-dev.20231019", + "@nestia/fetcher": ">=2.3.0-dev.20231020", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@nestjs/platform-express": ">=7.0.1", @@ -56,7 +56,7 @@ "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.0", "typescript": ">=4.8.0", - "typia": ">=5.2.2 <6.0.0" + "typia": ">=5.2.3 <6.0.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.0.0", diff --git a/packages/e2e/package.json b/packages/e2e/package.json index 4bcc20f02..618da1e03 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -40,7 +40,7 @@ "ts-node": "^10.9.1", "ts-patch": "^3.0.2", "typescript": "^5.2.2", - "typia": "^5.2.2" + "typia": "^5.2.3" }, "dependencies": { "chalk": "^4.1.2", diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 19d9b503e..77d3596c1 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/fetcher", - "version": "2.3.0-dev.20231019", + "version": "2.3.0-dev.20231020", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 6535b8021..591256c73 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -45,7 +45,7 @@ "typescript-transform-paths": "^3.4.6" }, "dependencies": { - "typia": "^5.2.2" + "typia": "^5.2.3" }, "files": [ "lib", diff --git a/packages/sdk/assets/config/nestia.config.ts b/packages/sdk/assets/config/nestia.config.ts index b9e757394..b329e9f7a 100644 --- a/packages/sdk/assets/config/nestia.config.ts +++ b/packages/sdk/assets/config/nestia.config.ts @@ -6,11 +6,13 @@ export const NESTIA_CONFIG: INestiaConfig = { /** * Accessor of controller classes. * - * You can specify it within two ways + * You can specify it within two ways. * * - Asynchronous function returning `INestApplication` instance * - Specify the path or directory of controller class files */ + // input: "src/controllers", + // input: "src/**/*.controller.ts", input: async () => { // change this to your own module @Module({ @@ -25,8 +27,6 @@ export const NESTIA_CONFIG: INestiaConfig = { // }) return app; }, - // input: "src/controllers", - // input: "src/**/*.controller.ts", /** * Building `swagger.json` is also possible. diff --git a/packages/sdk/package.json b/packages/sdk/package.json index f1b4353c8..d8df42186 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/sdk", - "version": "2.3.0-dev.20231019", + "version": "2.3.0-dev.20231020", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -35,7 +35,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^2.3.0-dev.20231019", + "@nestia/fetcher": "^2.3.0-dev.20231020", "cli": "^1.0.1", "get-function-location": "^2.0.0", "glob": "^7.2.0", @@ -45,16 +45,16 @@ "tsconfck": "^2.0.1", "tsconfig-paths": "^4.1.1", "tstl": "^2.5.13", - "typia": "^5.2.2" + "typia": "^5.2.3" }, "peerDependencies": { - "@nestia/fetcher": ">=2.3.0-dev.20231019", + "@nestia/fetcher": ">=2.3.0-dev.20231020", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", "ts-node": ">=10.6.0", "typescript": ">=4.8.0", - "typia": ">=5.2.2 <6.0.0" + "typia": ">=5.2.3 <6.0.0" }, "devDependencies": { "@nestjs/common": ">= 7.0.1", diff --git a/packages/sdk/src/INestiaConfig.ts b/packages/sdk/src/INestiaConfig.ts index 357aee284..3597a3993 100644 --- a/packages/sdk/src/INestiaConfig.ts +++ b/packages/sdk/src/INestiaConfig.ts @@ -14,7 +14,7 @@ export interface INestiaConfig { /** * Accessor of controller classes. * - * You can specify target controller classes within two ways + * You can specify target controller classes within two ways. * * - Asynchronous function returning `INestApplication` instance * - Specify the path or directory of controller class files diff --git a/packages/sdk/src/NestiaSdkApplication.ts b/packages/sdk/src/NestiaSdkApplication.ts index 944334c44..6aaeb9391 100644 --- a/packages/sdk/src/NestiaSdkApplication.ts +++ b/packages/sdk/src/NestiaSdkApplication.ts @@ -11,7 +11,10 @@ import { E2eGenerator } from "./generates/E2eGenerator"; import { SdkGenerator } from "./generates/SdkGenerator"; import { SwaggerGenerator } from "./generates/SwaggerGenerator"; import { IController } from "./structures/IController"; +import { IErrorReport } from "./structures/IErrorReport"; +import { INestiaProject } from "./structures/INestiaProject"; import { IRoute } from "./structures/IRoute"; +import { MapUtil } from "./utils/MapUtil"; export class NestiaSdkApplication { public constructor( @@ -104,11 +107,17 @@ export class NestiaSdkApplication { // ANALYZE REFLECTS const unique: WeakSet = new WeakSet(); const controllers: IController[] = []; + const project: INestiaProject = { + config: this.config, + input: await ConfigAnalyzer.input(this.config), + checker: null!, + errors: [], + }; console.log("Analyzing reflections"); for (const include of (await ConfigAnalyzer.input(this.config)).include) controllers.push( - ...(await ReflectAnalyzer.analyze( + ...(await ReflectAnalyzer.analyze(project)( unique, include.file, include.paths, @@ -146,7 +155,7 @@ export class NestiaSdkApplication { controllers.map((c) => c.file), this.compilerOptions, ); - const checker: ts.TypeChecker = program.getTypeChecker(); + project.checker = program.getTypeChecker(); const routeList: IRoute[] = []; for (const c of controllers) { @@ -155,15 +164,16 @@ export class NestiaSdkApplication { ); if (file === undefined) continue; routeList.push( - ...(await ControllerAnalyzer.analyze( - this.config, - checker, - file, - c, - )), + ...(await ControllerAnalyzer.analyze(project)(file, c)), ); } + // REPORT ERRORS + if (project.errors.length) { + report_errors(project.errors); + process.exit(-1); + } + // FIND IMPLICIT TYPES const implicit: IRoute[] = routeList.filter(is_implicit_return_typed); if (implicit.length > 0) @@ -181,7 +191,7 @@ export class NestiaSdkApplication { // DO GENERATE AccessorAnalyzer.analyze(routeList); - await archiver(checker)(config(this.config))(routeList); + await archiver(project.checker)(config(this.config))(routeList); } } @@ -207,4 +217,30 @@ const is_implicit_return_typed = (route: IRoute): boolean => { return true; }; +const report_errors = (errors: IErrorReport[]): void => { + // key: file + // key: controller + // key: function + // value: message + const map: Map>>> = new Map(); + for (const e of errors) { + const file = MapUtil.take(map, e.file, () => new Map()); + const controller = MapUtil.take(file, e.controller, () => new Map()); + const func = MapUtil.take(controller, e.function, () => new Set()); + func.add(e.message); + } + + console.log(""); + title("Nestia Error Report"); + for (const [file, cMap] of map) { + for (const [controller, fMap] of cMap) + for (const [func, messages] of fMap) { + const location: string = path.relative(process.cwd(), file); + console.log(`${location} - error on ${controller}.${func}()`); + for (const msg of messages) console.log(` - ${msg}`); + console.log(""); + } + } +}; + const VARIABLE = /[a-zA-Z_$0-9]/; diff --git a/packages/sdk/src/analyses/ControllerAnalyzer.ts b/packages/sdk/src/analyses/ControllerAnalyzer.ts index addc24d42..55c39acd6 100644 --- a/packages/sdk/src/analyses/ControllerAnalyzer.ts +++ b/packages/sdk/src/analyses/ControllerAnalyzer.ts @@ -5,13 +5,12 @@ import ts from "typescript"; import { CommentFactory } from "typia/lib/factories/CommentFactory"; -import { INestiaConfig } from "../INestiaConfig"; import { IController } from "../structures/IController"; -import { INormalizedInput } from "../structures/INormalizedInput"; +import { IErrorReport } from "../structures/IErrorReport"; +import { INestiaProject } from "../structures/INestiaProject"; import { IRoute } from "../structures/IRoute"; import { ITypeTuple } from "../structures/ITypeTuple"; import { PathUtil } from "../utils/PathUtil"; -import { ConfigAnalyzer } from "./ConfigAnalyzer"; import { ExceptionAnalyzer } from "./ExceptionAnalyzer"; import { GenericAnalyzer } from "./GenericAnalyzer"; import { ImportAnalyzer } from "./ImportAnalyzer"; @@ -19,340 +18,370 @@ import { PathAnalyzer } from "./PathAnalyzer"; import { SecurityAnalyzer } from "./SecurityAnalyzer"; export namespace ControllerAnalyzer { - export async function analyze( - config: INestiaConfig, - checker: ts.TypeChecker, - sourceFile: ts.SourceFile, - controller: IController, - ): Promise { - // FIND CONTROLLER CLASS - const input: INormalizedInput = await ConfigAnalyzer.input(config); - const ret: IRoute[] = []; - - ts.forEachChild(sourceFile, (node) => { - if ( - ts.isClassDeclaration(node) && - node.name?.escapedText === controller.name - ) { - // ANALYZE THE CONTROLLER - ret.push( - ..._Analyze_controller( - config, - input, - checker, - controller, - node, - ), - ); - return; - } - }); - return ret; - } + export const analyze = + (project: INestiaProject) => + async ( + sourceFile: ts.SourceFile, + controller: IController, + ): Promise => { + // FIND CONTROLLER CLASS + const ret: IRoute[] = []; + ts.forEachChild(sourceFile, (node) => { + if ( + ts.isClassDeclaration(node) && + node.name?.escapedText === controller.name + ) { + // ANALYZE THE CONTROLLER + ret.push(..._Analyze_controller(project)(controller, node)); + return; + } + }); + return ret; + }; /* --------------------------------------------------------- CLASS --------------------------------------------------------- */ - function _Analyze_controller( - config: INestiaConfig, - input: INormalizedInput, - checker: ts.TypeChecker, - controller: IController, - classNode: ts.ClassDeclaration, - ): IRoute[] { - const classType: ts.InterfaceType = checker.getTypeAtLocation( - classNode, - ) as ts.InterfaceType; - const genericDict: GenericAnalyzer.Dictionary = GenericAnalyzer.analyze( - checker, - classNode, - ); + const _Analyze_controller = + (project: INestiaProject) => + (controller: IController, classNode: ts.ClassDeclaration): IRoute[] => { + const classType: ts.InterfaceType = + project.checker.getTypeAtLocation( + classNode, + ) as ts.InterfaceType; + const genericDict: GenericAnalyzer.Dictionary = + GenericAnalyzer.analyze(project.checker, classNode); - const ret: IRoute[] = []; - for (const property of classType.getProperties()) { - // GET METHOD DECLARATION - const declaration: ts.Declaration | undefined = - (property.declarations || [])[0]; - if (!declaration || !ts.isMethodDeclaration(declaration)) continue; + const ret: IRoute[] = []; + for (const property of classType.getProperties()) { + // GET METHOD DECLARATION + const declaration: ts.Declaration | undefined = + (property.declarations || [])[0]; + if (!declaration || !ts.isMethodDeclaration(declaration)) + continue; - // IDENTIFIER MUST BE - const identifier = declaration.name; - if (!ts.isIdentifier(identifier)) continue; + // IDENTIFIER MUST BE + const identifier = declaration.name; + if (!ts.isIdentifier(identifier)) continue; - // ANALYZED WITH THE REFLECTED-FUNCTION - const runtime: IController.IFunction | undefined = - controller.functions.find( - (f) => f.name === identifier.escapedText, - ); - if (runtime === undefined) continue; + // ANALYZED WITH THE REFLECTED-FUNCTION + const runtime: IController.IFunction | undefined = + controller.functions.find( + (f) => f.name === identifier.escapedText, + ); + if (runtime === undefined) continue; - const routes: IRoute[] = _Analyze_function( - config, - input, - checker, - controller, - genericDict, - runtime, - declaration, - property, - ); - ret.push(...routes); - } - return ret; - } + const routes: IRoute[] = _Analyze_function(project)( + controller, + genericDict, + runtime, + declaration, + property, + ); + ret.push(...routes); + } + return ret; + }; /* --------------------------------------------------------- FUNCTION --------------------------------------------------------- */ - function _Analyze_function( - config: INestiaConfig, - input: INormalizedInput, - checker: ts.TypeChecker, - controller: IController, - genericDict: GenericAnalyzer.Dictionary, - func: IController.IFunction, - declaration: ts.MethodDeclaration, - symbol: ts.Symbol, - ): IRoute[] { - // PREPARE ASSETS - const type: ts.Type = checker.getTypeOfSymbolAtLocation( - symbol, - symbol.valueDeclaration!, - ); - const signature: ts.Signature | undefined = checker.getSignaturesOfType( - type, - ts.SignatureKind.Call, - )[0]; - if (signature === undefined) - throw new Error( - `Error on ControllerAnalyzer.analyze(): unable to get the signature from the ${controller.name}.${func.name}().`, + const _Analyze_function = + (project: INestiaProject) => + ( + controller: IController, + genericDict: GenericAnalyzer.Dictionary, + func: IController.IFunction, + declaration: ts.MethodDeclaration, + symbol: ts.Symbol, + ): IRoute[] => { + // PREPARE ASSETS + const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( + symbol, + symbol.valueDeclaration!, ); + const signature: ts.Signature | undefined = + project.checker.getSignaturesOfType( + type, + ts.SignatureKind.Call, + )[0]; + if (signature === undefined) { + project.errors.push({ + file: controller.file, + controller: controller.name, + function: func.name, + message: "unable to get the type signature.", + }); + return []; + } - // EXPLORE CHILDREN TYPES - const importDict: ImportAnalyzer.Dictionary = new HashMap(); - const parameters: IRoute.IParameter[] = func.parameters.map((param) => - _Analyze_parameter( - checker, + // EXPLORE CHILDREN TYPES + const importDict: ImportAnalyzer.Dictionary = new HashMap(); + const parameters: Array = + func.parameters.map( + (param) => + _Analyze_parameter(project)( + genericDict, + importDict, + controller, + func.name, + param, + signature.getParameters()[param.index], + )!, + ); + const outputType: ITypeTuple | null = ImportAnalyzer.analyze( + project.checker, genericDict, importDict, - controller, - func.name, - param, - signature.getParameters()[param.index], - ), - ); - const outputType: ITypeTuple | null = ImportAnalyzer.analyze( - checker, - genericDict, - importDict, - signature.getReturnType(), - ); - if (outputType === null) - throw new Error( - `Error on ControllerAnalyzer.analyze(): unnamed return type from ${controller.name}.${func.name}().`, + signature.getReturnType(), ); - else if ( - func.method === "HEAD" && - outputType.typeName !== "void" && - outputType.typeName !== "undefined" - ) - throw new Error( - `Error on ControllerAnalyzer.analyze(): HEAD method must return void type - ${controller.name}.${func.name}().`, + if (outputType === null || outputType.typeName === "__type") { + project.errors.push({ + file: controller.file, + controller: controller.name, + function: func.name, + message: "implicit (unnamed) return type.", + }); + return []; + } else if ( + func.method === "HEAD" && + outputType.typeName !== "void" && + outputType.typeName !== "undefined" + ) { + project.errors.push({ + file: controller.file, + controller: controller.name, + function: func.name, + message: `HEAD method must return void type.`, + }); + return []; + } + + const exceptions = ExceptionAnalyzer.analyze(project)( + genericDict, + project.config.propagate === true ? importDict : new HashMap(), + )( + controller, + func, + )(declaration); + const imports: [string, string[]][] = importDict + .toJSON() + .map((pair) => [pair.first, pair.second.toJSON()]); + + // PARSE COMMENT TAGS + const jsDocTags = signature.getJsDocTags(); + const security: Record[] = SecurityAnalyzer.merge( + ...controller.security, + ...func.security, + ...jsDocTags + .filter((tag) => tag.name === "security") + .map((tag) => + (tag.text ?? []).map((text) => { + const line: string[] = text.text + .split(" ") + .filter((s) => s.trim()) + .filter((s) => !!s.length); + if (line.length === 0) return {}; + return { + [line[0]]: line.slice(1), + }; + }), + ) + .flat(), ); - const exceptions = ExceptionAnalyzer.analyze(checker)( - genericDict, - config.propagate === true ? importDict : new HashMap(), - )(func)(declaration); - const imports: [string, string[]][] = importDict - .toJSON() - .map((pair) => [pair.first, pair.second.toJSON()]); + // CONSTRUCT COMMON DATA + const common: Omit = { + ...func, + parameters: parameters.filter( + (p) => p !== null, + ) as IRoute.IParameter[], + output: { + type: outputType.type, + typeName: outputType.typeName, + contentType: func.contentType, + }, + imports, + status: func.status, + symbol: { + class: controller.name, + function: func.name, + }, + location: (() => { + const file = declaration.getSourceFile(); + const { line, character } = + file.getLineAndCharacterOfPosition(declaration.pos); + return `${path.relative(process.cwd(), file.fileName)}:${ + line + 1 + }:${character + 1}`; + })(), + description: CommentFactory.description(symbol), + operationId: jsDocTags + .find(({ name }) => name === "operationId") + ?.text?.[0].text.split(" ")[0] + .trim(), + jsDocTags: jsDocTags, + setHeaders: jsDocTags + .filter( + (t) => + t.text?.length && + t.text[0].text && + (t.name === "setHeader" || + t.name === "assignHeaders"), + ) + .map((t) => + t.name === "setHeader" + ? { + type: "setter", + source: t.text![0].text.split(" ")[0].trim(), + target: t.text![0].text.split(" ")[1]?.trim(), + } + : { + type: "assigner", + source: t.text![0].text, + }, + ), + security, + exceptions, + }; - // PARSE COMMENT TAGS - const jsDocTags = signature.getJsDocTags(); - const security: Record[] = SecurityAnalyzer.merge( - ...controller.security, - ...func.security, - ...jsDocTags - .filter((tag) => tag.name === "security") - .map((tag) => - (tag.text ?? []).map((text) => { - const line: string[] = text.text - .split(" ") - .filter((s) => s.trim()) - .filter((s) => !!s.length); - if (line.length === 0) return {}; - return { - [line[0]]: line.slice(1), - }; - }), - ) - .flat(), - ); + // CONFIGURE PATHS + const pathList: Set = new Set(); + const versions: Array = _Analyze_versions( + project.input.versioning === undefined + ? undefined + : func.versions ?? + controller.versions ?? + (project.input.versioning?.defaultVersion !== + undefined + ? Array.isArray( + project.input.versioning?.defaultVersion, + ) + ? project.input.versioning?.defaultVersion + : [project.input.versioning?.defaultVersion] + : undefined) ?? + undefined, + ); + for (const prefix of controller.prefixes) + for (const cPath of controller.paths) + for (const filePath of func.paths) + pathList.add( + PathAnalyzer.join(prefix, cPath, filePath), + ); - // CONSTRUCT COMMON DATA - const common: Omit = { - ...func, - parameters, - output: { - type: outputType.type, - typeName: outputType.typeName, - contentType: func.contentType, - }, - imports, - status: func.status, - symbol: { - class: controller.name, - function: func.name, - }, - location: (() => { - const file = declaration.getSourceFile(); - const { line, character } = file.getLineAndCharacterOfPosition( - declaration.pos, - ); - return `${path.relative(process.cwd(), file.fileName)}:${ - line + 1 - }:${character + 1}`; - })(), - description: CommentFactory.description(symbol), - operationId: jsDocTags - .find(({ name }) => name === "operationId") - ?.text?.[0].text.split(" ")[0] - .trim(), - jsDocTags: jsDocTags, - setHeaders: jsDocTags - .filter( - (t) => - t.text?.length && - t.text[0].text && - (t.name === "setHeader" || t.name === "assignHeaders"), + return [...pathList] + .map((individual) => + PathAnalyzer.combinate(project.input.globalPrefix)( + [...versions].map((v) => + v === null + ? null + : project.input.versioning?.prefix?.length + ? `${project.input.versioning.prefix}${v}` + : v, + ), + )({ + method: func.method, + path: individual, + }), ) - .map((t) => - t.name === "setHeader" - ? { - type: "setter", - source: t.text![0].text.split(" ")[0].trim(), - target: t.text![0].text.split(" ")[1]?.trim(), - } - : { - type: "assigner", - source: t.text![0].text, - }, - ), - security, - exceptions, + .flat() + .map((path) => ({ + ...common, + path: PathAnalyzer.escape( + path, + () => "ControllerAnalyzer.analyze()", + ), + accessors: [...PathUtil.accessors(path), func.name], + })); }; - // CONFIGURE PATHS - const pathList: Set = new Set(); - const versions: Array = _Analyze_versions( - input.versioning === undefined - ? undefined - : func.versions ?? - controller.versions ?? - (input.versioning?.defaultVersion !== undefined - ? Array.isArray(input.versioning?.defaultVersion) - ? input.versioning?.defaultVersion - : [input.versioning?.defaultVersion] - : undefined) ?? - undefined, - ); - for (const prefix of controller.prefixes) - for (const cPath of controller.paths) - for (const filePath of func.paths) - pathList.add(PathAnalyzer.join(prefix, cPath, filePath)); - - return [...pathList] - .map((individual) => - PathAnalyzer.combinate(input.globalPrefix)( - [...versions].map((v) => - v === null - ? null - : input.versioning?.prefix?.length - ? `${input.versioning.prefix}${v}` - : v, - ), - )({ - method: func.method, - path: individual, - }), - ) - .flat() - .map((path) => ({ - ...common, - path: PathAnalyzer.escape( - path, - () => "ControllerAnalyzer.analyze()", - ), - accessors: [...PathUtil.accessors(path), func.name], - })); - } + const _Analyze_parameter = + (project: INestiaProject) => + ( + genericDict: GenericAnalyzer.Dictionary, + importDict: ImportAnalyzer.Dictionary, + controller: IController, + funcName: string, + param: IController.IParameter, + symbol: ts.Symbol, + ): IRoute.IParameter | null => { + const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( + symbol, + symbol.valueDeclaration!, + ); + const name: string = symbol.getEscapedName().toString(); - function _Analyze_parameter( - checker: ts.TypeChecker, - genericDict: GenericAnalyzer.Dictionary, - importDict: ImportAnalyzer.Dictionary, - controller: IController, - funcName: string, - param: IController.IParameter, - symbol: ts.Symbol, - ): IRoute.IParameter { - const type: ts.Type = checker.getTypeOfSymbolAtLocation( - symbol, - symbol.valueDeclaration!, - ); - const name: string = symbol.getEscapedName().toString(); - const method: string = `${controller.name}.${funcName}()`; + const optional: boolean = + !!project.checker.symbolToParameterDeclaration( + symbol, + undefined, + undefined, + )?.questionToken; - const optional: boolean = !!checker.symbolToParameterDeclaration( - symbol, - undefined, - undefined, - )?.questionToken; + const errors: IErrorReport[] = []; - // DO NOT SUPPORT BODY PARAMETER - if (param.category === "body" && param.field !== undefined) - throw new Error( - `Error on ${method}: nestia does not support body field specification. ` + - `Therefore, erase the ${method}#${name} parameter and ` + - `re-define a new body decorator accepting full structured message.`, - ); - else if (optional === true && param.category !== "query") - throw new Error( - `Error on ${method}: nestia does not support optional parameter except query parameter. ` + - `Therefore, erase question mark on ${method}#${name} parameter, ` + - `or re-define a new method without the "name" parameter.`, - ); - else if ( - optional === true && - param.category === "query" && - param.field === undefined - ) - throw new Error( - `Error on ${method}: nestia does not support optional query parameter without field specification. ` + - `Therefore, erase question mark on ${method}#${name} parameter, ` + - `or re-define re-define parameters for each query parameters.`, - ); + // DO NOT SUPPORT BODY PARAMETER + if (param.category === "body" && param.field !== undefined) + errors.push({ + file: controller.file, + controller: controller.name, + function: funcName, + message: + `nestia does not support body field specification. ` + + `Therefore, erase the "${name}" parameter and ` + + `re-define a new body decorator accepting full structured message.`, + }); + if (optional === true && param.category !== "query") + errors.push({ + file: controller.file, + controller: controller.name, + function: funcName, + message: + `nestia does not support optional parameter except query parameter. ` + + `Therefore, erase question mark on the "${name}" parameter, ` + + `or re-define a new method without the "${name}" parameter.`, + }); + if ( + optional === true && + param.category === "query" && + param.field === undefined + ) + errors.push({ + file: controller.file, + controller: controller.name, + function: funcName, + message: + `nestia does not support optional query parameter without field specification. ` + + `Therefore, erase question mark on the "${name}" parameter, ` + + `or re-define re-define parameters for each query parameters.`, + }); - // GET TYPE NAME - const tuple: ITypeTuple | null = ImportAnalyzer.analyze( - checker, - genericDict, - importDict, - type, - ); - if (tuple === null) - throw new Error( - `Error on ${method}: unnamed parameter type from ${method}#${name}.`, + // GET TYPE NAME + const tuple: ITypeTuple | null = ImportAnalyzer.analyze( + project.checker, + genericDict, + importDict, + type, ); - return { - ...param, - name, - optional, - type: tuple.type, - typeName: tuple.typeName, + if (tuple === null || tuple.typeName === "__type") + errors.push({ + file: controller.file, + controller: controller.name, + function: funcName, + message: `implicit (unnamed) parameter type from "${name}".`, + }); + if (errors.length) { + project.errors.push(...errors); + return null; + } + return { + ...param, + name, + optional, + type: tuple!.type, + typeName: tuple!.typeName, + }; }; - } function _Analyze_versions( value: diff --git a/packages/sdk/src/analyses/ExceptionAnalyzer.ts b/packages/sdk/src/analyses/ExceptionAnalyzer.ts index 316a310c5..97e397c66 100644 --- a/packages/sdk/src/analyses/ExceptionAnalyzer.ts +++ b/packages/sdk/src/analyses/ExceptionAnalyzer.ts @@ -2,6 +2,7 @@ import path from "path"; import ts from "typescript"; import { IController } from "../structures/IController"; +import { INestiaProject } from "../structures/INestiaProject"; import { IRoute } from "../structures/IRoute"; import { ITypeTuple } from "../structures/ITypeTuple"; import { GenericAnalyzer } from "./GenericAnalyzer"; @@ -9,12 +10,12 @@ import { ImportAnalyzer } from "./ImportAnalyzer"; export namespace ExceptionAnalyzer { export const analyze = - (checker: ts.TypeChecker) => + (project: INestiaProject) => ( genericDict: GenericAnalyzer.Dictionary, importDict: ImportAnalyzer.Dictionary, ) => - (func: IController.IFunction) => + (controller: IController, func: IController.IFunction) => ( declaration: ts.MethodDeclaration, ): Record => { @@ -24,19 +25,20 @@ export namespace ExceptionAnalyzer { > = {} as any; for (const decorator of declaration.modifiers ?? []) if (ts.isDecorator(decorator)) - analyzeTyped(checker)(genericDict, importDict)(func)( - output, - )(decorator); + analyzeTyped(project)(genericDict, importDict)( + controller, + func, + )(output)(decorator); return output; }; const analyzeTyped = - (checker: ts.TypeChecker) => + (project: INestiaProject) => ( genericDict: GenericAnalyzer.Dictionary, importDict: ImportAnalyzer.Dictionary, ) => - (func: IController.IFunction) => + (controller: IController, func: IController.IFunction) => ( output: Record< number | "2XX" | "3XX" | "4XX" | "5XX", @@ -51,7 +53,7 @@ export namespace ExceptionAnalyzer { // CHECK SIGNATURE const signature: ts.Signature | undefined = - checker.getResolvedSignature(decorator.expression); + project.checker.getResolvedSignature(decorator.expression); if (!signature || !signature.declaration) return false; else if ( path @@ -62,19 +64,33 @@ export namespace ExceptionAnalyzer { // GET TYPE INFO const node: ts.TypeNode = decorator.expression.typeArguments![0]; - const type: ts.Type = checker.getTypeFromTypeNode(node); - if (type.isTypeParameter()) - throw new Error( - "Error on @nestia.core.TypedException(): non-specified generic argument.", - ); + const type: ts.Type = project.checker.getTypeFromTypeNode(node); + if (type.isTypeParameter()) { + project.errors.push({ + file: controller.file, + controller: controller.name, + function: func.name, + message: + "TypedException() without generic argument specification.", + }); + return false; + } const tuple: ITypeTuple | null = ImportAnalyzer.analyze( - checker, + project.checker, genericDict, importDict, type, ); - if (tuple === null) return false; + if (tuple === null || tuple.typeName === "__type") { + project.errors.push({ + file: controller.file, + controller: controller.name, + function: func.name, + message: "TypeException() with implicit (unnamed) type.", + }); + return false; + } // DO ASSIGN const matched: IController.IException[] = Object.entries( diff --git a/packages/sdk/src/analyses/ReflectAnalyzer.ts b/packages/sdk/src/analyses/ReflectAnalyzer.ts index cd1988757..14ac09308 100644 --- a/packages/sdk/src/analyses/ReflectAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectAnalyzer.ts @@ -4,6 +4,8 @@ import "reflect-metadata"; import { equal } from "tstl/ranges/module"; import { IController } from "../structures/IController"; +import { IErrorReport } from "../structures/IErrorReport"; +import { INestiaProject } from "../structures/INestiaProject"; import { ParamCategory } from "../structures/ParamCategory"; import { ArrayUtil } from "../utils/ArrayUtil"; import { PathAnalyzer } from "./PathAnalyzer"; @@ -14,105 +16,107 @@ type IModule = { }; export namespace ReflectAnalyzer { - export async function analyze( - unique: WeakSet, - file: string, - prefixes: string[], - target?: Function, - ): Promise { - const module: IModule = await (async () => { - try { - return await import(file); - } catch (exp) { - console.log( - ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", - ); - console.log(`Error on "${file}" file. Check your code.`); - console.log(exp); - console.log( - ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", + export const analyze = + (project: INestiaProject) => + async ( + unique: WeakSet, + file: string, + prefixes: string[], + target?: Function, + ): Promise => { + const module: IModule = await (async () => { + try { + return await import(file); + } catch (exp) { + console.log( + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", + ); + console.log(`Error on "${file}" file. Check your code.`); + console.log(exp); + console.log( + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", + ); + process.exit(-1); + } + })(); + const ret: IController[] = []; + + for (const [key, value] of Object.entries(module)) { + if (typeof value !== "function" || unique.has(value)) continue; + else if ((target ?? value) !== value) continue; + else unique.add(value); + + const result: IController | null = _Analyze_controller(project)( + file, + key, + value, + prefixes, ); - process.exit(-1); + if (result !== null) ret.push(result); } - })(); - const ret: IController[] = []; - - for (const [key, value] of Object.entries(module)) { - if (typeof value !== "function" || unique.has(value)) continue; - else if ((target ?? value) !== value) continue; - else unique.add(value); - - const result: IController | null = _Analyze_controller( - file, - key, - value, - prefixes, - ); - if (result !== null) ret.push(result); - } - return ret; - } + return ret; + }; /* --------------------------------------------------------- CONTROLLER --------------------------------------------------------- */ - function _Analyze_controller( - file: string, - name: string, - creator: any, - prefixes: string[], - ): IController | null { - //---- - // VALIDATIONS - //---- - // MUST BE TYPE OF A CREATOR WHO HAS THE CONSTRUCTOR - if ( - !( - creator instanceof Function && - creator.constructor instanceof Function + const _Analyze_controller = + (project: INestiaProject) => + ( + file: string, + name: string, + creator: any, + prefixes: string[], + ): IController | null => { + //---- + // VALIDATIONS + //---- + // MUST BE TYPE OF A CREATOR WHO HAS THE CONSTRUCTOR + if ( + !( + creator instanceof Function && + creator.constructor instanceof Function + ) ) - ) - return null; - // MUST HAVE THOSE MATADATA - else if ( - ArrayUtil.has( - Reflect.getMetadataKeys(creator), - Constants.PATH_METADATA, - Constants.HOST_METADATA, - Constants.SCOPE_OPTIONS_METADATA, - ) === false - ) - return null; - - //---- - // CONSTRUCTION - //---- - // BASIC INFO - const meta: IController = { - file, - name, - functions: [], - prefixes, - paths: _Get_paths(creator), - versions: _Get_versions(creator), - security: _Get_securities(creator), - swaggerTgas: - Reflect.getMetadata("swagger/apiUseTags", creator) ?? [], - }; + return null; + // MUST HAVE THOSE MATADATA + else if ( + ArrayUtil.has( + Reflect.getMetadataKeys(creator), + Constants.PATH_METADATA, + Constants.HOST_METADATA, + Constants.SCOPE_OPTIONS_METADATA, + ) === false + ) + return null; - // PARSE CHILDREN DATA - for (const tuple of _Get_prototype_entries(creator)) { - const child: IController.IFunction | null = _Analyze_function( - creator.prototype, - meta, - ...tuple, - ); - if (child !== null) meta.functions.push(child); - } + //---- + // CONSTRUCTION + //---- + // BASIC INFO + const meta: IController = { + file, + name, + functions: [], + prefixes, + paths: _Get_paths(creator), + versions: _Get_versions(creator), + security: _Get_securities(creator), + swaggerTgas: + Reflect.getMetadata("swagger/apiUseTags", creator) ?? [], + }; - // RETURNS - return meta; - } + // PARSE CHILDREN DATA + for (const tuple of _Get_prototype_entries(creator)) { + const child: IController.IFunction | null = _Analyze_function( + project, + )(creator.prototype, meta, ...tuple); + if (child !== null) meta.functions.push(child); + } + + // RETURNS + return meta; + }; function _Get_prototype_entries(creator: any): Array<[string, unknown]> { const keyList = Object.getOwnPropertyNames(creator.prototype); @@ -172,132 +176,152 @@ export namespace ReflectAnalyzer { /* --------------------------------------------------------- FUNCTION --------------------------------------------------------- */ - function _Analyze_function( - classProto: any, - controller: IController, - name: string, - proto: any, - ): IController.IFunction | null { - //---- - // VALIDATIONS - //---- - // MUST BE TYPE OF A FUNCTION - if (!(proto instanceof Function)) return null; - // MUST HAVE THOSE METADATE - else if ( - ArrayUtil.has( - Reflect.getMetadataKeys(proto), - Constants.PATH_METADATA, - Constants.METHOD_METADATA, - ) === false - ) - return null; - - //---- - // CONSTRUCTION - //---- - // BASIC INFO - const encrypted: boolean = - Reflect.getMetadata(Constants.INTERCEPTORS_METADATA, proto)?.[0] - ?.constructor?.name === "EncryptedRouteInterceptor"; - const query: boolean = - Reflect.getMetadata(Constants.INTERCEPTORS_METADATA, proto)?.[0] - ?.constructor?.name === "TypedQueryRouteInterceptor"; - const method: string = - METHODS[Reflect.getMetadata(Constants.METHOD_METADATA, proto)]; - if (method === undefined || method === "OPTIONS") return null; - - const parameters: IController.IParameter[] = (() => { - const nestParameters: NestParameters | undefined = - Reflect.getMetadata( - Constants.ROUTE_ARGS_METADATA, - classProto.constructor, - name, - ); - if (nestParameters === undefined) return []; - - const output: IController.IParameter[] = []; - for (const tuple of Object.entries(nestParameters)) { - const child: IController.IParameter | null = _Analyze_parameter( - ...tuple, - ); - if (child !== null) output.push(child); - } - return output.sort((x, y) => x.index - y.index); - })(); - - // VALIDATE BODY - const body: IController.IParameter | undefined = parameters.find( - (param) => param.category === "body", - ); - if (body !== undefined && (method === "GET" || method === "HEAD")) - throw new Error( - `Error on ${controller.name}.${name}(): "body" parameter cannot be used in the "GET" or "HEAD" method`, + const _Analyze_function = + (project: INestiaProject) => + ( + classProto: any, + controller: IController, + name: string, + proto: any, + ): IController.IFunction | null => { + //---- + // VALIDATIONS + //---- + // MUST BE TYPE OF A FUNCTION + if (!(proto instanceof Function)) return null; + // MUST HAVE THOSE METADATE + else if ( + ArrayUtil.has( + Reflect.getMetadataKeys(proto), + Constants.PATH_METADATA, + Constants.METHOD_METADATA, + ) === false + ) + return null; + + const errors: IErrorReport[] = []; + + //---- + // CONSTRUCTION + //---- + // BASIC INFO + const encrypted: boolean = + Reflect.getMetadata(Constants.INTERCEPTORS_METADATA, proto)?.[0] + ?.constructor?.name === "EncryptedRouteInterceptor"; + const query: boolean = + Reflect.getMetadata(Constants.INTERCEPTORS_METADATA, proto)?.[0] + ?.constructor?.name === "TypedQueryRouteInterceptor"; + const method: string = + METHODS[Reflect.getMetadata(Constants.METHOD_METADATA, proto)]; + if (method === undefined || method === "OPTIONS") return null; + + const parameters: IController.IParameter[] = (() => { + const nestParameters: NestParameters | undefined = + Reflect.getMetadata( + Constants.ROUTE_ARGS_METADATA, + classProto.constructor, + name, + ); + if (nestParameters === undefined) return []; + + const output: IController.IParameter[] = []; + for (const tuple of Object.entries(nestParameters)) { + const child: IController.IParameter | null = + _Analyze_parameter(...tuple); + if (child !== null) output.push(child); + } + return output.sort((x, y) => x.index - y.index); + })(); + + // VALIDATE BODY + const body: IController.IParameter | undefined = parameters.find( + (param) => param.category === "body", ); + if (body !== undefined && (method === "GET" || method === "HEAD")) { + errors.push({ + file: controller.file, + controller: controller.name, + function: name, + message: `"body" parameter cannot be used in the "${method}" method.`, + }); + return null; + } - // DO CONSTRUCT - const meta: IController.IFunction = { - name, - method: method === "ALL" ? "POST" : method, - paths: _Get_paths(proto), - versions: _Get_versions(proto), - parameters, - status: Reflect.getMetadata(Constants.HTTP_CODE_METADATA, proto), - encrypted, - contentType: encrypted - ? "text/plain" - : query - ? "application/x-www-form-urlencoded" - : Reflect.getMetadata(Constants.HEADERS_METADATA, proto)?.find( - (h: Record) => - typeof h?.name === "string" && - typeof h?.value === "string" && - h.name.toLowerCase() === "content-type", - )?.value ?? "application/json", - security: _Get_securities(proto), - exceptions: _Get_exceptions(proto), - swaggerTags: [ - ...new Set([ - ...controller.swaggerTgas, - ...(Reflect.getMetadata("swagger/apiUseTags", proto) ?? []), - ]), - ], - }; - - // VALIDATE PATH ARGUMENTS - for (const controllerLocation of controller.paths) - for (const metaLocation of meta.paths) { - // NORMALIZE LOCATION - const location: string = PathAnalyzer.join( - controllerLocation, - metaLocation, - ); + // DO CONSTRUCT + const meta: IController.IFunction = { + name, + method: method === "ALL" ? "POST" : method, + paths: _Get_paths(proto), + versions: _Get_versions(proto), + parameters, + status: Reflect.getMetadata( + Constants.HTTP_CODE_METADATA, + proto, + ), + encrypted, + contentType: encrypted + ? "text/plain" + : query + ? "application/x-www-form-urlencoded" + : Reflect.getMetadata( + Constants.HEADERS_METADATA, + proto, + )?.find( + (h: Record) => + typeof h?.name === "string" && + typeof h?.value === "string" && + h.name.toLowerCase() === "content-type", + )?.value ?? "application/json", + security: _Get_securities(proto), + exceptions: _Get_exceptions(proto), + swaggerTags: [ + ...new Set([ + ...controller.swaggerTgas, + ...(Reflect.getMetadata("swagger/apiUseTags", proto) ?? + []), + ]), + ], + }; - // LIST UP PARAMETERS - const binded: string[] = PathAnalyzer.parameters( - location, - () => `${controller.name}.${name}()`, - ).sort(); - - const parameters: string[] = meta.parameters - .filter((param) => param.category === "param") - .map((param) => param.field!) - .sort(); - - // DO VALIDATE - if (equal(binded, parameters) === false) - throw new Error( - `Error on ${ - controller.name - }.${name}(): binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( - ", ", - )}], parameters: [${parameters.join(", ")}])`, + // VALIDATE PATH ARGUMENTS + for (const controllerLocation of controller.paths) + for (const metaLocation of meta.paths) { + // NORMALIZE LOCATION + const location: string = PathAnalyzer.join( + controllerLocation, + metaLocation, ); - } - // RETURNS - return meta; - } + // LIST UP PARAMETERS + const binded: string[] = PathAnalyzer.parameters( + location, + () => `${controller.name}.${name}()`, + ).sort(); + + const parameters: string[] = meta.parameters + .filter((param) => param.category === "param") + .map((param) => param.field!) + .sort(); + + // DO VALIDATE + if (equal(binded, parameters) === false) + errors.push({ + file: controller.file, + controller: controller.name, + function: name, + message: `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( + ", ", + )}], parameters: [${parameters.join(", ")}]).`, + }); + } + + // RETURNS + if (errors.length) { + project.errors.push(...errors); + return null; + } + return meta; + }; /* --------------------------------------------------------- PARAMETER diff --git a/packages/sdk/src/structures/IErrorReport.ts b/packages/sdk/src/structures/IErrorReport.ts new file mode 100644 index 000000000..f0aadd69e --- /dev/null +++ b/packages/sdk/src/structures/IErrorReport.ts @@ -0,0 +1,6 @@ +export interface IErrorReport { + file: string; + controller: string; + function: string; + message: string; +} diff --git a/packages/sdk/src/structures/INestiaProject.ts b/packages/sdk/src/structures/INestiaProject.ts new file mode 100644 index 000000000..b612322e7 --- /dev/null +++ b/packages/sdk/src/structures/INestiaProject.ts @@ -0,0 +1,12 @@ +import ts from "typescript"; + +import { INestiaConfig } from "../INestiaConfig"; +import { IErrorReport } from "./IErrorReport"; +import { INormalizedInput } from "./INormalizedInput"; + +export interface INestiaProject { + config: INestiaConfig; + input: INormalizedInput; + checker: ts.TypeChecker; + errors: IErrorReport[]; +} diff --git a/test/features/body-error-implicit/nestia.config.ts b/test/features/body-error-implicit/nestia.config.ts new file mode 100644 index 000000000..2594a40d0 --- /dev/null +++ b/test/features/body-error-implicit/nestia.config.ts @@ -0,0 +1,15 @@ +import { INestiaConfig } from "@nestia/sdk"; + +export const NESTIA_CONFIG: INestiaConfig = { + input: ["src/controllers"], + output: "src/api", + swagger: { + output: "swagger.json", + security: { + bearer: { + type: "apiKey", + }, + }, + }, +}; +export default NESTIA_CONFIG; diff --git a/test/features/body-error-implicit/src/api/HttpError.ts b/test/features/body-error-implicit/src/api/HttpError.ts new file mode 100644 index 000000000..5df328ae4 --- /dev/null +++ b/test/features/body-error-implicit/src/api/HttpError.ts @@ -0,0 +1 @@ +export { HttpError } from "@nestia/fetcher"; diff --git a/test/features/body-error-implicit/src/api/IConnection.ts b/test/features/body-error-implicit/src/api/IConnection.ts new file mode 100644 index 000000000..107bdb8f8 --- /dev/null +++ b/test/features/body-error-implicit/src/api/IConnection.ts @@ -0,0 +1 @@ +export type { IConnection } from "@nestia/fetcher"; diff --git a/test/features/body-error-implicit/src/api/Primitive.ts b/test/features/body-error-implicit/src/api/Primitive.ts new file mode 100644 index 000000000..60d394424 --- /dev/null +++ b/test/features/body-error-implicit/src/api/Primitive.ts @@ -0,0 +1 @@ +export type { Primitive } from "@nestia/fetcher"; diff --git a/test/features/body-error-implicit/src/api/functional/bbs/articles/index.ts b/test/features/body-error-implicit/src/api/functional/bbs/articles/index.ts new file mode 100644 index 000000000..136e0842f --- /dev/null +++ b/test/features/body-error-implicit/src/api/functional/bbs/articles/index.ts @@ -0,0 +1,58 @@ +/** + * @packageDocumentation + * @module api.functional.bbs.articles + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection, Primitive } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; +import type { Format } from "typia/lib/tags/Format"; + +import type { __type } from "../../../../controllers/BbsArticlesController"; + +/** + * @controller BbsArticlesController.update + * @path PUT /bbs/articles/:id + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function update( + connection: IConnection, + id: string & Format<"uuid">, + input: update.Input, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...(connection.headers ?? {}), + "Content-Type": "application/json", + }, + }, + { + ...update.METADATA, + path: update.path(id), + } as const, + input, + ); +} +export namespace update { + export type Input = Primitive<__type>; + + export const METADATA = { + method: "PUT", + path: "/bbs/articles/:id", + request: { + type: "application/json", + encrypted: false + }, + response: { + type: "application/json", + encrypted: false, + }, + status: null, + } as const; + + export const path = (id: string & Format<"uuid">): string => { + return `/bbs/articles/${encodeURIComponent(id ?? "null")}`; + } +} \ No newline at end of file diff --git a/test/features/body-error-implicit/src/api/functional/bbs/index.ts b/test/features/body-error-implicit/src/api/functional/bbs/index.ts new file mode 100644 index 000000000..efe5ba769 --- /dev/null +++ b/test/features/body-error-implicit/src/api/functional/bbs/index.ts @@ -0,0 +1,7 @@ +/** + * @packageDocumentation + * @module api.functional.bbs + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +export * as articles from "./articles"; \ No newline at end of file diff --git a/test/features/body-error-implicit/src/api/functional/health/index.ts b/test/features/body-error-implicit/src/api/functional/health/index.ts new file mode 100644 index 000000000..22f66eceb --- /dev/null +++ b/test/features/body-error-implicit/src/api/functional/health/index.ts @@ -0,0 +1,42 @@ +/** + * @packageDocumentation + * @module api.functional.health + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; + +/** + * @controller HealthController.get + * @path GET /health + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function get( + connection: IConnection, +): Promise { + return PlainFetcher.fetch( + connection, + { + ...get.METADATA, + path: get.path(), + } as const, + ); +} +export namespace get { + + export const METADATA = { + method: "GET", + path: "/health", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: null, + } as const; + + export const path = (): string => { + return `/health`; + } +} \ No newline at end of file diff --git a/test/features/body-error-implicit/src/api/functional/index.ts b/test/features/body-error-implicit/src/api/functional/index.ts new file mode 100644 index 000000000..a78415209 --- /dev/null +++ b/test/features/body-error-implicit/src/api/functional/index.ts @@ -0,0 +1,8 @@ +/** + * @packageDocumentation + * @module api.functional + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +export * as bbs from "./bbs"; +export * as health from "./health"; \ No newline at end of file diff --git a/test/features/body-error-implicit/src/api/index.ts b/test/features/body-error-implicit/src/api/index.ts new file mode 100644 index 000000000..1705f43c8 --- /dev/null +++ b/test/features/body-error-implicit/src/api/index.ts @@ -0,0 +1,4 @@ +import * as api from "./module"; + +export * from "./module"; +export default api; diff --git a/test/features/body-error-implicit/src/api/module.ts b/test/features/body-error-implicit/src/api/module.ts new file mode 100644 index 000000000..dbb6e9a51 --- /dev/null +++ b/test/features/body-error-implicit/src/api/module.ts @@ -0,0 +1,5 @@ +export type * from "./IConnection"; +export type * from "./Primitive"; +export * from "./HttpError"; + +export * as functional from "./functional"; diff --git a/test/features/body-error-implicit/src/controllers/BbsArticlesController.ts b/test/features/body-error-implicit/src/controllers/BbsArticlesController.ts new file mode 100644 index 000000000..2271b301c --- /dev/null +++ b/test/features/body-error-implicit/src/controllers/BbsArticlesController.ts @@ -0,0 +1,19 @@ +import core from "@nestia/core"; +import { Body, Controller } from "@nestjs/common"; +import { tags } from "typia"; + +@Controller("bbs/articles") +export class BbsArticlesController { + @core.TypedRoute.Put(":id") + public async update( + @core.TypedParam("id") id: string & tags.Format<"uuid">, + @core.TypedBody() + input: { + title: string & tags.MaxLength<10>; + content: string & tags.MaxLength<100>; + }, + ): Promise { + id; + input; + } +} diff --git a/test/features/body-error-implicit/src/controllers/HealthController.ts b/test/features/body-error-implicit/src/controllers/HealthController.ts new file mode 100644 index 000000000..a9ce3563b --- /dev/null +++ b/test/features/body-error-implicit/src/controllers/HealthController.ts @@ -0,0 +1,9 @@ +import { Controller } from "@nestjs/common"; + +import core from "@nestia/core"; + +@Controller("health") +export class HealthController { + @core.TypedRoute.Get() + public get(): void {} +} diff --git a/test/features/body-error-implicit/tsconfig.json b/test/features/body-error-implicit/tsconfig.json new file mode 100644 index 000000000..886b107c7 --- /dev/null +++ b/test/features/body-error-implicit/tsconfig.json @@ -0,0 +1,95 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */// "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./bin", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. *//* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typia/lib/transform" }, + { "transform": "@nestia/core/lib/transform" }, + ], + } + } \ No newline at end of file diff --git a/test/features/body-error-optional/nestia.config.ts b/test/features/body-error-optional/nestia.config.ts new file mode 100644 index 000000000..2594a40d0 --- /dev/null +++ b/test/features/body-error-optional/nestia.config.ts @@ -0,0 +1,15 @@ +import { INestiaConfig } from "@nestia/sdk"; + +export const NESTIA_CONFIG: INestiaConfig = { + input: ["src/controllers"], + output: "src/api", + swagger: { + output: "swagger.json", + security: { + bearer: { + type: "apiKey", + }, + }, + }, +}; +export default NESTIA_CONFIG; diff --git a/test/features/body-error-optional/src/api/structures/IBbsArticle.ts b/test/features/body-error-optional/src/api/structures/IBbsArticle.ts new file mode 100644 index 000000000..c919f3bdd --- /dev/null +++ b/test/features/body-error-optional/src/api/structures/IBbsArticle.ts @@ -0,0 +1,43 @@ +export interface IBbsArticle extends IBbsArticle.IStore { + /** + * @format uuid + */ + id: string; + + /** + * @format date-time + */ + created_at: string; +} +export namespace IBbsArticle { + export interface IStore { + /** + * @minLength 3 + * @maxLength 50 + */ + title: string; + body: string; + files: IAttachmentFile[]; + } + + export type IUpdate = Partial; +} + +export interface IAttachmentFile { + /** + * @minLengt 1 + * @maxLength 255 + */ + name: string | null; + + /** + * @minLength 1 + * @maxLength 8 + */ + extension: string | null; + + /** + * @format url + */ + url: string; +} diff --git a/test/features/body-error-optional/src/controllers/HealthController.ts b/test/features/body-error-optional/src/controllers/HealthController.ts new file mode 100644 index 000000000..a9ce3563b --- /dev/null +++ b/test/features/body-error-optional/src/controllers/HealthController.ts @@ -0,0 +1,9 @@ +import { Controller } from "@nestjs/common"; + +import core from "@nestia/core"; + +@Controller("health") +export class HealthController { + @core.TypedRoute.Get() + public get(): void {} +} diff --git a/test/features/body-error-optional/src/controllers/TypedBodyController.ts b/test/features/body-error-optional/src/controllers/TypedBodyController.ts new file mode 100644 index 000000000..388391b18 --- /dev/null +++ b/test/features/body-error-optional/src/controllers/TypedBodyController.ts @@ -0,0 +1,40 @@ +import core from "@nestia/core"; +import { Controller, Request } from "@nestjs/common"; +import typia, { tags } from "typia"; + +import { IBbsArticle } from "../api/structures/IBbsArticle"; + +@Controller("body") +export class TypedBodyController { + /** + * Store an article. + * + * @param request Request object from express. Must be disappeared in SDK + * @param input Content to store + * @returns Newly archived article + * + * @author Samchon + * @warning This is an fake API + */ + @core.TypedRoute.Post() + public async store( + @Request() request: any, + @core.TypedBody() input: IBbsArticle.IStore, + ): Promise { + request; + const output: IBbsArticle = { + ...typia.random(), + ...input, + }; + return output; + } + + @core.TypedRoute.Put(":id") + public async update( + @core.TypedParam("id") id: string & tags.Format<"uuid">, + @core.TypedBody() input?: IBbsArticle.IUpdate, + ): Promise { + id; + input; + } +} diff --git a/test/features/body-error-optional/tsconfig.json b/test/features/body-error-optional/tsconfig.json new file mode 100644 index 000000000..886b107c7 --- /dev/null +++ b/test/features/body-error-optional/tsconfig.json @@ -0,0 +1,95 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */// "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./bin", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. *//* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typia/lib/transform" }, + { "transform": "@nestia/core/lib/transform" }, + ], + } + } \ No newline at end of file diff --git a/test/features/body-error-property/nestia.config.ts b/test/features/body-error-property/nestia.config.ts new file mode 100644 index 000000000..2594a40d0 --- /dev/null +++ b/test/features/body-error-property/nestia.config.ts @@ -0,0 +1,15 @@ +import { INestiaConfig } from "@nestia/sdk"; + +export const NESTIA_CONFIG: INestiaConfig = { + input: ["src/controllers"], + output: "src/api", + swagger: { + output: "swagger.json", + security: { + bearer: { + type: "apiKey", + }, + }, + }, +}; +export default NESTIA_CONFIG; diff --git a/test/features/body-error-property/src/controllers/BbsArticlesController.ts b/test/features/body-error-property/src/controllers/BbsArticlesController.ts new file mode 100644 index 000000000..b648e7672 --- /dev/null +++ b/test/features/body-error-property/src/controllers/BbsArticlesController.ts @@ -0,0 +1,15 @@ +import core from "@nestia/core"; +import { Body, Controller } from "@nestjs/common"; +import { tags } from "typia"; + +@Controller("bbs/articles") +export class BbsArticlesController { + @core.TypedRoute.Put(":id") + public async update( + @core.TypedParam("id") id: string & tags.Format<"uuid">, + @Body("content") content: string, + ): Promise { + id; + content; + } +} diff --git a/test/features/body-error-property/src/controllers/HealthController.ts b/test/features/body-error-property/src/controllers/HealthController.ts new file mode 100644 index 000000000..a9ce3563b --- /dev/null +++ b/test/features/body-error-property/src/controllers/HealthController.ts @@ -0,0 +1,9 @@ +import { Controller } from "@nestjs/common"; + +import core from "@nestia/core"; + +@Controller("health") +export class HealthController { + @core.TypedRoute.Get() + public get(): void {} +} diff --git a/test/features/body-error-property/tsconfig.json b/test/features/body-error-property/tsconfig.json new file mode 100644 index 000000000..886b107c7 --- /dev/null +++ b/test/features/body-error-property/tsconfig.json @@ -0,0 +1,95 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */// "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./bin", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. *//* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typia/lib/transform" }, + { "transform": "@nestia/core/lib/transform" }, + ], + } + } \ No newline at end of file diff --git a/test/features/body/src/api/functional/body/index.ts b/test/features/body/src/api/functional/body/index.ts index a0000c957..1daa0d656 100644 --- a/test/features/body/src/api/functional/body/index.ts +++ b/test/features/body/src/api/functional/body/index.ts @@ -6,6 +6,7 @@ //================================================================ import type { IConnection, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; +import type { Format } from "typia/lib/tags/Format"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -61,4 +62,51 @@ export namespace store { export const path = (): string => { return `/body`; } +} + +/** + * @controller TypedBodyController.update + * @path PUT /body/:id + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function update( + connection: IConnection, + id: string & Format<"uuid">, + input: update.Input, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...(connection.headers ?? {}), + "Content-Type": "application/json", + }, + }, + { + ...update.METADATA, + path: update.path(id), + } as const, + input, + ); +} +export namespace update { + export type Input = Primitive>; + + export const METADATA = { + method: "PUT", + path: "/body/:id", + request: { + type: "application/json", + encrypted: false + }, + response: { + type: "application/json", + encrypted: false, + }, + status: null, + } as const; + + export const path = (id: string & Format<"uuid">): string => { + return `/body/${encodeURIComponent(id ?? "null")}`; + } } \ No newline at end of file diff --git a/test/features/body/src/api/structures/IBbsArticle.ts b/test/features/body/src/api/structures/IBbsArticle.ts index aa8085b02..c919f3bdd 100644 --- a/test/features/body/src/api/structures/IBbsArticle.ts +++ b/test/features/body/src/api/structures/IBbsArticle.ts @@ -19,6 +19,8 @@ export namespace IBbsArticle { body: string; files: IAttachmentFile[]; } + + export type IUpdate = Partial; } export interface IAttachmentFile { diff --git a/test/features/body/src/controllers/TypedBodyController.ts b/test/features/body/src/controllers/TypedBodyController.ts index dac5e6c9e..01f82835e 100644 --- a/test/features/body/src/controllers/TypedBodyController.ts +++ b/test/features/body/src/controllers/TypedBodyController.ts @@ -1,7 +1,6 @@ -import { Controller, Request } from "@nestjs/common"; -import typia from "typia"; - import core from "@nestia/core"; +import { Controller, Request } from "@nestjs/common"; +import typia, { tags } from "typia"; import { IBbsArticle } from "@api/lib/structures/IBbsArticle"; @@ -9,11 +8,11 @@ import { IBbsArticle } from "@api/lib/structures/IBbsArticle"; export class TypedBodyController { /** * Store an article. - * + * * @param request Request object from express. Must be disappeared in SDK * @param input Content to store * @returns Newly archived article - * + * * @author Samchon * @warning This is an fake API */ @@ -29,4 +28,13 @@ export class TypedBodyController { }; return output; } + + @core.TypedRoute.Put(":id") + public async update( + @core.TypedParam("id") id: string & tags.Format<"uuid">, + @core.TypedBody() input: IBbsArticle.IUpdate, + ): Promise { + id; + input; + } } diff --git a/test/features/body/src/test/features/api/automated/test_api_body_update.ts b/test/features/body/src/test/features/api/automated/test_api_body_update.ts new file mode 100644 index 000000000..9a56124f6 --- /dev/null +++ b/test/features/body/src/test/features/api/automated/test_api_body_update.ts @@ -0,0 +1,16 @@ +import type { Primitive, Resolved } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; + +export const test_api_body_update = async ( + connection: api.IConnection +): Promise => { + await api.functional.body.update( + connection, + typia.random>>(), + typia.random>>(), + ); +}; \ No newline at end of file diff --git a/test/features/body/swagger.json b/test/features/body/swagger.json index 127e324d5..adeeea4d4 100644 --- a/test/features/body/swagger.json +++ b/test/features/body/swagger.json @@ -242,6 +242,56 @@ ], "x-nestia-method": "POST" } + }, + "/body/{id}": { + "put": { + "tags": [], + "parameters": [ + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "format": "uuid", + "x-typia-typeTags": [ + { + "target": "string", + "name": "Format<\"uuid\">", + "kind": "format", + "value": "uuid", + "validate": "/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test($input)", + "exclusive": [ + "format", + "pattern" + ] + } + ] + }, + "description": "", + "required": true + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PartialIBbsArticle.IStore" + } + } + }, + "required": true, + "x-nestia-encrypted": false + }, + "responses": { + "201": { + "description": "", + "x-nestia-encrypted": false + } + }, + "x-nestia-namespace": "body.update", + "x-nestia-jsDocTags": [], + "x-nestia-method": "PUT" + } } }, "components": { @@ -760,6 +810,72 @@ "files" ], "x-typia-jsDocTags": [] + }, + "PartialIBbsArticle.IStore": { + "type": "object", + "properties": { + "title": { + "x-typia-jsDocTags": [ + { + "name": "minLength", + "text": [ + { + "text": "3", + "kind": "text" + } + ] + }, + { + "name": "maxLength", + "text": [ + { + "text": "50", + "kind": "text" + } + ] + } + ], + "x-typia-required": false, + "x-typia-optional": true, + "type": "string", + "maxLength": 50, + "minLength": 3, + "x-typia-typeTags": [ + { + "name": "MinLength<3>", + "target": "string", + "kind": "minLength", + "value": 3, + "validate": "3 <= $input.length", + "exclusive": true + }, + { + "name": "MaxLength<50>", + "target": "string", + "kind": "maxLength", + "value": 50, + "validate": "$input.length <= 50", + "exclusive": true + } + ] + }, + "body": { + "x-typia-required": false, + "x-typia-optional": true, + "type": "string" + }, + "files": { + "x-typia-required": false, + "x-typia-optional": true, + "type": "array", + "items": { + "$ref": "#/components/schemas/IAttachmentFile" + } + } + }, + "nullable": false, + "description": "Make all properties in T optional", + "x-typia-jsDocTags": [] } }, "securitySchemes": { diff --git a/test/features/distribute-assert/packages/api/package.json b/test/features/distribute-assert/packages/api/package.json index 0d17b0d90..23bc88aa4 100644 --- a/test/features/distribute-assert/packages/api/package.json +++ b/test/features/distribute-assert/packages/api/package.json @@ -34,6 +34,6 @@ }, "dependencies": { "@nestia/fetcher": "file:../../../../../packages/fetcher/nestia-fetcher-0.0.0-dev.20991231.tgz", - "typia": "^5.2.2" + "typia": "^5.2.3" } } \ No newline at end of file diff --git a/test/features/distribute-json/packages/api/package.json b/test/features/distribute-json/packages/api/package.json index 0d17b0d90..23bc88aa4 100644 --- a/test/features/distribute-json/packages/api/package.json +++ b/test/features/distribute-json/packages/api/package.json @@ -34,6 +34,6 @@ }, "dependencies": { "@nestia/fetcher": "file:../../../../../packages/fetcher/nestia-fetcher-0.0.0-dev.20991231.tgz", - "typia": "^5.2.2" + "typia": "^5.2.3" } } \ No newline at end of file diff --git a/test/features/distribute/packages/api/package.json b/test/features/distribute/packages/api/package.json index 0d17b0d90..23bc88aa4 100644 --- a/test/features/distribute/packages/api/package.json +++ b/test/features/distribute/packages/api/package.json @@ -34,6 +34,6 @@ }, "dependencies": { "@nestia/fetcher": "file:../../../../../packages/fetcher/nestia-fetcher-0.0.0-dev.20991231.tgz", - "typia": "^5.2.2" + "typia": "^5.2.3" } } \ No newline at end of file diff --git a/test/features/route-error-implicit/nestia.config.ts b/test/features/route-error-implicit/nestia.config.ts new file mode 100644 index 000000000..2594a40d0 --- /dev/null +++ b/test/features/route-error-implicit/nestia.config.ts @@ -0,0 +1,15 @@ +import { INestiaConfig } from "@nestia/sdk"; + +export const NESTIA_CONFIG: INestiaConfig = { + input: ["src/controllers"], + output: "src/api", + swagger: { + output: "swagger.json", + security: { + bearer: { + type: "apiKey", + }, + }, + }, +}; +export default NESTIA_CONFIG; diff --git a/test/features/route-error-implicit/src/controllers/BbsArticlesController.ts b/test/features/route-error-implicit/src/controllers/BbsArticlesController.ts new file mode 100644 index 000000000..dc9717b1d --- /dev/null +++ b/test/features/route-error-implicit/src/controllers/BbsArticlesController.ts @@ -0,0 +1,16 @@ +import core from "@nestia/core"; +import { Controller } from "@nestjs/common"; +import { tags } from "typia"; + +@Controller("bbs/articles") +export class BbsArticlesController { + @core.TypedRoute.Get(":id") + public async at(@core.TypedParam("id") id: string & tags.Format<"uuid">) { + return { + id, + title: "something", + body: "content", + created_at: new Date().toISOString(), + }; + } +} diff --git a/test/features/route-error-implicit/src/controllers/HealthController.ts b/test/features/route-error-implicit/src/controllers/HealthController.ts new file mode 100644 index 000000000..a9ce3563b --- /dev/null +++ b/test/features/route-error-implicit/src/controllers/HealthController.ts @@ -0,0 +1,9 @@ +import { Controller } from "@nestjs/common"; + +import core from "@nestia/core"; + +@Controller("health") +export class HealthController { + @core.TypedRoute.Get() + public get(): void {} +} diff --git a/test/features/route-error-implicit/tsconfig.json b/test/features/route-error-implicit/tsconfig.json new file mode 100644 index 000000000..886b107c7 --- /dev/null +++ b/test/features/route-error-implicit/tsconfig.json @@ -0,0 +1,95 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */// "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./bin", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. *//* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typia/lib/transform" }, + { "transform": "@nestia/core/lib/transform" }, + ], + } + } \ No newline at end of file diff --git a/test/package.json b/test/package.json index fd6418a46..434b041f4 100644 --- a/test/package.json +++ b/test/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@nestia/test", - "version": "2.3.0-dev.20231019", + "version": "2.3.0-dev.20231020", "description": "Test program of Nestia", "main": "index.js", "scripts": { @@ -34,12 +34,12 @@ "ts-patch": "v3.0.2", "typescript": "^5.2.2", "typescript-transform-paths": "^3.4.4", - "typia": "^5.2.2", + "typia": "^5.2.3", "uuid": "^9.0.0", "nestia": "^4.5.0", - "@nestia/core": "^2.3.0-dev.20231019", + "@nestia/core": "^2.3.0-dev.20231020", "@nestia/e2e": "^0.3.6", - "@nestia/fetcher": "^2.3.0-dev.20231019", - "@nestia/sdk": "^2.3.0-dev.20231019" + "@nestia/fetcher": "^2.3.0-dev.20231020", + "@nestia/sdk": "^2.3.0-dev.20231020" } } \ No newline at end of file