@nestia/sdk
Version:
Nestia SDK and Swagger generator
305 lines (295 loc) • 10.4 kB
text/typescript
import {
type Expression,
NodeFlags,
type Statement,
SyntaxKind,
type TypeNode,
factory,
} from "@ttsc/factory";
import { IJsDocTagInfo } from "typia";
import { IdentifierFactory } from "../../factories/IdentifierFactory";
import { INestiaProject } from "../../structures/INestiaProject";
import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute";
import { StringUtil } from "../../utils/StringUtil";
import { FilePrinter } from "./FilePrinter";
import { ImportDictionary } from "./ImportDictionary";
import { SdkImportWizard } from "./SdkImportWizard";
import { SdkWebSocketNamespaceProgrammer } from "./SdkWebSocketNamespaceProgrammer";
import { SdkWebSocketParameterProgrammer } from "./SdkWebSocketParameterProgrammer";
export namespace SdkWebSocketRouteProgrammer {
export const write =
(project: INestiaProject) =>
(importer: ImportDictionary) =>
(route: ITypedWebSocketRoute): Statement[] => [
FilePrinter.description(
writeFunction(project)(importer)(route),
writeDescription(route),
),
SdkWebSocketNamespaceProgrammer.write(project)(importer)(route),
];
const writeDescription = (route: ITypedWebSocketRoute): string => {
// MAIN DESCRIPTION
const comments: string[] = route.description
? route.description.split("\n")
: [];
// COMMENT TAGS
const tags: IJsDocTagInfo[] = route.jsDocTags.filter(
(tag) =>
tag.name !== "param" ||
[...route.pathParameters, ...(route.query ? [route.query] : [])]
.filter((p) => p.category === "param" || p.category === "query")
.some((p) => p.name === tag.text?.[0]?.text),
);
if (tags.length !== 0) {
const content: string[] = tags.map((t) =>
t.text?.length
? `@${t.name} ${t.text.map((e) => e.text).join("")}`
: `@${t.name}`,
);
comments.push("", ...new Set(content));
}
// POSTFIX
if (!!comments.length) comments.push("");
comments.push(
`@controller ${route.controller.class.name}.${route.name}`,
`@path ${route.path}`,
`@nestia Generated by Nestia - https://github.com/samchon/nestia`,
);
return comments.join("\n");
};
const writeFunction =
(project: INestiaProject) =>
(importer: ImportDictionary) =>
(route: ITypedWebSocketRoute): Statement => {
const connection: string = StringUtil.escapeDuplicate([route.name])(
"connection",
);
return factory.createFunctionDeclaration(
[
factory.createModifier(SyntaxKind.ExportKeyword),
factory.createModifier(SyntaxKind.AsyncKeyword),
],
undefined,
route.name,
undefined,
[
IdentifierFactory.parameter(
connection,
factory.createTypeReferenceNode(
SdkImportWizard.IConnection(importer),
[factory.createTypeReferenceNode(`${route.name}.Header`)],
),
),
...SdkWebSocketParameterProgrammer.getParameterDeclarations({
project,
route,
provider: true,
prefix: true,
}),
],
factory.createTypeReferenceNode("Promise", [
factory.createTypeReferenceNode(`${route.name}.Output`),
]),
factory.createBlock(
writeFunctionBody(project)(importer)(route)(connection),
true,
),
);
};
const writeFunctionBody =
(project: INestiaProject) =>
(importer: ImportDictionary) =>
(route: ITypedWebSocketRoute) =>
(connection: string): Statement[] => {
const access = (key: string) =>
project.config.keyword === true
? factory.createPropertyAccessExpression(
factory.createIdentifier("props"),
key,
)
: factory.createIdentifier(key);
return [
local("url")(factory.createTypeReferenceNode("string"))(
joinPath(
connection,
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier(route.name),
"path",
),
[],
project.config.keyword === true &&
SdkWebSocketParameterProgrammer.isPathEmpty(route) === false
? [factory.createIdentifier("props")]
: SdkWebSocketParameterProgrammer.getEntries({
project,
route,
provider: false,
prefix: true,
}).map((p) => factory.createIdentifier(p.key)),
),
),
),
local("connector")(
factory.createTypeReferenceNode(
importer.external({
declaration: false,
file: "tgrid",
type: "element",
name: "WebSocketConnector",
}),
[
factory.createTypeReferenceNode(`${route.name}.Header`),
factory.createTypeReferenceNode(`${route.name}.Provider`),
factory.createTypeReferenceNode(`${route.name}.Listener`),
],
),
)(
factory.createNewExpression(
factory.createIdentifier(
importer.external({
declaration: false,
file: "tgrid",
type: "element",
name: "WebSocketConnector",
}),
),
undefined,
[
factory.createAsExpression(
factory.createBinaryExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier(connection),
"headers",
),
factory.createToken(SyntaxKind.QuestionQuestionToken),
factory.createObjectLiteralExpression([], false),
),
factory.createKeywordTypeNode(SyntaxKind.AnyKeyword),
),
access("provider"),
],
),
),
factory.createExpressionStatement(
factory.createAwaitExpression(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("connector"),
"connect",
),
undefined,
[factory.createIdentifier("url")],
),
),
),
local("driver")(
factory.createTypeReferenceNode(
importer.external({
declaration: true,
file: "tgrid",
type: "element",
name: "Driver",
}),
[factory.createTypeReferenceNode(`${route.name}.Listener`)],
),
)(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("connector"),
"getDriver",
),
undefined,
undefined,
),
),
factory.createReturnStatement(
factory.createObjectLiteralExpression(
[
factory.createShorthandPropertyAssignment("connector"),
factory.createShorthandPropertyAssignment("driver"),
factory.createPropertyAssignment(
factory.createIdentifier("reconnect"),
factory.createArrowFunction(
[factory.createToken(SyntaxKind.AsyncKeyword)],
undefined,
[],
undefined,
factory.createToken(SyntaxKind.EqualsGreaterThanToken),
factory.createAwaitExpression(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("connector"),
factory.createIdentifier("connect"),
),
undefined,
[factory.createIdentifier("url")],
),
),
),
),
],
true,
),
),
];
};
}
const local = (name: string) => (type: TypeNode) => (expression: Expression) =>
factory.createVariableStatement(
[],
factory.createVariableDeclarationList(
[factory.createVariableDeclaration(name, undefined, type, expression)],
NodeFlags.Const,
),
);
const joinPath = (connection: string, caller: Expression) =>
factory.createTemplateExpression(factory.createTemplateHead("", ""), [
factory.createTemplateSpan(
factory.createConditionalExpression(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier(connection),
factory.createIdentifier("host"),
),
factory.createIdentifier("endsWith"),
),
undefined,
[factory.createStringLiteral("/")],
),
factory.createToken(SyntaxKind.QuestionToken),
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier(connection),
factory.createIdentifier("host"),
),
factory.createIdentifier("substring"),
),
undefined,
[
factory.createNumericLiteral("0"),
factory.createBinaryExpression(
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier(connection),
factory.createIdentifier("host"),
),
factory.createIdentifier("length"),
),
factory.createToken(SyntaxKind.MinusToken),
factory.createNumericLiteral("1"),
),
],
),
factory.createToken(SyntaxKind.ColonToken),
factory.createPropertyAccessExpression(
factory.createIdentifier(connection),
factory.createIdentifier("host"),
),
),
factory.createTemplateMiddle(""),
),
factory.createTemplateSpan(caller, factory.createTemplateTail("")),
]);