@nestia/core
Version:
Super-fast validation decorators of NestJS
121 lines (113 loc) • 3.75 kB
text/typescript
import path from "path";
import ts from "typescript";
import { INestiaTransformContext } from "../options/INestiaTransformProject";
export namespace WebSocketRouteTransformer {
export const validate = (props: {
context: INestiaTransformContext;
decorator: ts.Decorator;
method: ts.MethodDeclaration;
}): ts.Decorator => {
if (!ts.isCallExpression(props.decorator.expression))
return props.decorator;
// CHECK SIGNATURE
const signature: ts.Signature | undefined =
props.context.checker.getResolvedSignature(props.decorator.expression);
if (!signature || !signature.declaration) return props.decorator;
else if (isLocated(signature) === false) return props.decorator;
const errors: ts.DiagnosticWithLocation[] = [];
let accepted: boolean = false;
const report = (node: ts.Node, message: string) => {
errors.push(
(ts as any).createDiagnosticForNode(node, {
category: ts.DiagnosticCategory.Error,
key: "nestia.core.WebSocketRoute",
code: "(nestia.core.WebSocketRoute)" as any,
message,
}),
);
};
props.method.parameters.forEach((param) => {
const paramDecos: ts.Decorator[] = (param.modifiers ?? []).filter((m) =>
ts.isDecorator(m),
) as ts.Decorator[];
const category: string | null = (() => {
if (paramDecos.length !== 1) return null;
const decorator: ts.Decorator = paramDecos[0];
const signature: ts.Signature | undefined = ts.isCallExpression(
decorator.expression,
)
? props.context.checker.getResolvedSignature(decorator.expression)
: undefined;
if (signature === undefined || isLocated(signature) === false)
return null;
return (
decorator.expression.getText().split(".").at(-1)?.split("(")[0] ??
null
);
})();
if (category === null)
report(
param,
`parameter ${JSON.stringify(param.name.getText())} is not decorated with nested function of WebSocketRoute module.`,
);
else if (category === "Acceptor") {
accepted = true;
if (
param.type
?.getText()
.split("<")[0]
?.split(".")
.at(-1)
?.startsWith("WebSocketAcceptor") !== true
)
report(
param,
`parameter ${JSON.stringify(param.name.getText())} must have WebSocketAcceptor<Header, Provider, Listener> type.`,
);
} else if (category === "Driver") {
if (
param.type
?.getText()
.split("<")[0]
?.split(".")
.at(-1)
?.startsWith("Driver") !== true
)
report(
param,
`parameter ${JSON.stringify(param.name.getText())} must have Driver<Listener> type.`,
);
}
});
if (accepted === false)
report(
props.method,
`method ${JSON.stringify(props.method.name.getText())} must have at least one parameter decorated by @WebSocketRoute.Acceptor().`,
);
for (const e of errors) props.context.extras.addDiagnostic(e);
return props.decorator;
};
}
const isLocated = (signature: ts.Signature) => {
if (!signature.declaration) return false;
const location: string = path.resolve(
signature.declaration.getSourceFile().fileName,
);
return (
location.indexOf(LIB_PATH) !== -1 || location.indexOf(MONO_PATH) !== -1
);
};
const LIB_PATH = path.join(
"@nestia",
"core",
"lib",
"decorators",
`WebSocketRoute.d.ts`,
);
const MONO_PATH = path.join(
"packages",
"core",
"lib",
"decorators",
`WebSocketRoute.d.ts`,
);