UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

262 lines (248 loc) 8.31 kB
import ts from "typescript"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { Metadata } from "typia/lib/schemas/metadata/Metadata"; import { INestiaProject } from "../../structures/INestiaProject"; import { IReflectType } from "../../structures/IReflectType"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute"; import { FilePrinter } from "./FilePrinter"; import { ImportDictionary } from "./ImportDictionary"; import { SdkHttpParameterProgrammer } from "./SdkHttpParameterProgrammer"; import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace SdkAliasCollection { export const name = ({ type }: { type: IReflectType }): ts.TypeNode => ts.factory.createTypeReferenceNode( type.name, type.typeArguments ? type.typeArguments.map((a) => name({ type: a })) : undefined, ); export const from = (project: INestiaProject) => (importer: ImportDictionary) => (metadata: Metadata) => SdkTypeProgrammer.write(project)(importer)(metadata); export const httpProps = (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => ts.factory.createTypeLiteralNode( SdkHttpParameterProgrammer.getEntries({ project, importer, route, body: true, prefix: false, }) .map((e) => { const signature: ts.PropertySignature = ts.factory.createPropertySignature( undefined, e.key, e.required ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), e.type, ); const description: string | null = e.parameter.description ?? route.jsDocTags ?.find( (tag) => tag.name === "param" && tag.text?.[0]?.kind === "parameterName" && tag.text?.[0]?.text === e.key, ) ?.text?.find((t) => t.kind === "text")?.text ?? null; return description?.length ? [ ts.factory.createIdentifier("\n") as any, FilePrinter.description(signature, description), ] : [signature]; }) .flat(), ); export const websocketProps = (route: ITypedWebSocketRoute): ts.TypeNode => ts.factory.createTypeLiteralNode([ ...route.pathParameters.map((p) => ts.factory.createPropertySignature( undefined, p.name, undefined, SdkAliasCollection.name(p), ), ), ...(route.query ? [ ts.factory.createPropertySignature( undefined, "query", undefined, ts.factory.createTypeReferenceNode("Query"), ), ] : []), ts.factory.createPropertySignature( undefined, "provider", undefined, ts.factory.createTypeReferenceNode("Provider"), ), ]); export const headers = (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter.IHeaders): ts.TypeNode => { if (project.config.clone === true) return from(project)(importer)(param.metadata); const type: ts.TypeNode = name(param); if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( importer.external({ file: "typia", declaration: true, type: "element", name: "Resolved", }), [type], ); }; export const query = (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter.IQuery): ts.TypeNode => { if (project.config.clone === true) return from(project)(importer)(param.metadata); const type: ts.TypeNode = name(param); if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( importer.external({ file: "typia", declaration: true, type: "element", name: "Resolved", }), [type], ); }; export const body = (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter.IBody): ts.TypeNode => { if (project.config.clone === true) { const type: ts.TypeNode = from(project)(importer)(param.metadata); return param.contentType === "multipart/form-data" ? formDataInput(importer)(type) : type; } const type: ts.TypeNode = name(param); if (param.contentType === "multipart/form-data") return formDataInput(importer)(type); else if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( importer.external({ file: "typia", declaration: true, type: "element", name: param.contentType === "application/json" || param.encrypted === true ? "Primitive" : "Resolved", }), [type], ); }; export const response = (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => { const schema = (p: { metadata: Metadata; type: IReflectType }) => p.metadata.size() === 0 ? TypeFactory.keyword("void") : project.config.clone === true ? from(project)(importer)(p.metadata) : project.config.primitive !== false ? ts.factory.createTypeReferenceNode( importer.external({ file: "typia", declaration: true, type: "element", name: route.success.contentType === "application/json" || route.success.encrypted === true ? "Primitive" : "Resolved", }), [name(p)], ) : name(p); if (project.config.propagate !== true) return schema(route.success); const branches: IBranch[] = [ { status: String( route.success.status ?? (route.method === "POST" ? 201 : 200), ), type: schema(route.success), }, ...Object.entries(route.exceptions).map(([status, value]) => ({ status, type: schema(value), })), ]; return ts.factory.createTypeReferenceNode( importer.external({ file: "@nestia/fetcher", declaration: true, type: "element", name: "IPropagation", }), [ ts.factory.createTypeLiteralNode( branches.map((b) => ts.factory.createPropertySignature( undefined, ts.factory.createNumericLiteral(b.status), undefined, b.type, ), ), ), ...(route.success.status ? [ ts.factory.createLiteralTypeNode( ts.factory.createNumericLiteral(route.success.status), ), ] : []), ], ); }; export const responseBody = (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => response({ ...project, config: { ...project.config, propagate: false, }, })(importer)(route); const formDataInput = (importer: ImportDictionary) => (type: ts.TypeNode) => ts.factory.createTypeReferenceNode( importer.external({ file: "@nestia/fetcher", declaration: true, type: "element", name: "FormDataInput", }), [type], ); } interface IBranch { status: string; type: ts.TypeNode; }