typedoc-plugin-zod
Version:
TypeDoc plugin which replaces z.infer<typeof x> with the inferred type
83 lines (82 loc) • 3.6 kB
JavaScript
import { Converter, DeclarationReflection, IntrinsicType, makeRecursiveVisitor, ReferenceType, ReflectionKind, TypeScript as ts, } from "typedoc";
export function load(app) {
// schema type alias -> referenced validator
const schemaTypes = new Map();
app.converter.on(Converter.EVENT_CREATE_DECLARATION, onCreateDeclaration);
app.converter.on(Converter.EVENT_RESOLVE_BEGIN, (context) => {
const typeCleanup = makeRecursiveVisitor({
reflection: (type) => {
context.project.removeReflection(type.declaration);
},
});
for (const [inferredType, refOrig] of schemaTypes) {
if (refOrig.reflection instanceof DeclarationReflection
&& refOrig.reflection.type instanceof ReferenceType) {
refOrig.reflection.type.typeArguments?.forEach((t) => t.visit(typeCleanup));
refOrig.reflection.type.typeArguments = [
ReferenceType.createResolvedReference(inferredType.name, inferredType, context.project),
];
inferredType.comment ?? (inferredType.comment = refOrig.reflection.comment?.clone());
}
}
schemaTypes.clear();
}, 2000);
function onCreateDeclaration(context, refl) {
if ("deferConversion" in context.converter) {
// In 0.28, the `type` member of type aliases isn't set yet, so we need
// to wait until deferred conversion steps are happening to check it.
// This isn't really what that hook was intended for originally, but seems
// like an appropriate use for this plugin.
context.converter.deferConversion(() => {
resolveTypeAliasTypes(context, refl);
});
}
else {
resolveTypeAliasTypes(context, refl);
}
}
function resolveTypeAliasTypes(context, refl) {
// Check if this is a type alias which points to a zod schema
// This is a rather unfortunate way to do this check... Zod's type structure
// has changed somewhat between v3 and v4, so we have to check several names.
// TypeDoc doesn't track scoped imports (zod vs zod/v4) so that doesn't need
// to be checked here.
if (!refl.kindOf(ReflectionKind.TypeAlias)
|| refl.type?.type !== "reference"
|| refl.type.package !== "zod"
|| !["TypeOf", "input", "output"].includes(refl.type.qualifiedName)) {
return;
}
const originalRef = refl.type.typeArguments?.[0]?.visit({
query: (t) => t.queryType,
});
const declaration = getSymbolFromReflection(context, refl)
?.getDeclarations()
?.find(ts.isTypeAliasDeclaration);
if (!declaration)
return;
const type = context.getTypeAtLocation(declaration);
refl.type.visit(makeRecursiveVisitor({
reflection: (type) => {
context.project.removeReflection(type.declaration);
},
}));
if (type) {
refl.type = context.converter.convertType(context.withScope(refl), type);
}
else {
refl.type = new IntrinsicType("any");
}
if (originalRef) {
schemaTypes.set(refl, originalRef);
}
}
}
function getSymbolFromReflection(context, refl) {
if ("getSymbolFromReflection" in context) {
// 0.28
return context.getSymbolFromReflection(refl);
}
// <0.28
return refl.project.getSymbolFromReflection(refl);
}