openapi-typescript
Version:
Convert OpenAPI 3.0 & 3.1 schemas to TypeScript
93 lines (85 loc) • 3.23 kB
text/typescript
import ts, { InterfaceDeclaration, TypeLiteralNode } from "typescript";
import { NEVER, STRING, tsModifiers, tsRecord } from "../lib/ts.js";
import { createRef, debug } from "../lib/utils.js";
import { GlobalContext, OpenAPI3 } from "../types.js";
import transformComponentsObject from "./components-object.js";
import transformPathsObject from "./paths-object.js";
import transformSchemaObject from "./schema-object.js";
import transformWebhooksObject from "./webhooks-object.js";
type SchemaTransforms = keyof Pick<
OpenAPI3,
"paths" | "webhooks" | "components" | "$defs"
>;
const transformers: Record<
SchemaTransforms,
(node: any, options: GlobalContext) => ts.TypeNode // eslint-disable-line @typescript-eslint/no-explicit-any
> = {
paths: transformPathsObject,
webhooks: transformWebhooksObject,
components: transformComponentsObject,
$defs: (node, options) =>
transformSchemaObject(node, { path: createRef(["$defs"]), ctx: options }),
};
export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) {
const type: ts.Node[] = [];
for (const root of Object.keys(transformers) as SchemaTransforms[]) {
const emptyObj = ts.factory.createTypeAliasDeclaration(
/* modifiers */ tsModifiers({ export: true }),
/* name */ root,
/* typeParameters */ undefined,
/* type */ tsRecord(STRING, NEVER),
);
if (schema[root] && typeof schema[root] === "object") {
const rootT = performance.now();
const subType = transformers[root](schema[root], ctx);
if ((subType as ts.TypeLiteralNode).members?.length) {
type.push(
ctx.exportType
? ts.factory.createTypeAliasDeclaration(
/* modifiers */ tsModifiers({ export: true }),
/* name */ root,
/* typeParameters */ undefined,
/* type */ subType,
)
: ts.factory.createInterfaceDeclaration(
/* modifiers */ tsModifiers({ export: true }),
/* name */ root,
/* typeParameters */ undefined,
/* heritageClauses */ undefined,
/* members */ (subType as TypeLiteralNode).members,
),
);
debug(`${root} done`, "ts", performance.now() - rootT);
} else {
type.push(emptyObj);
debug(`${root} done (skipped)`, "ts", 0);
}
} else {
type.push(emptyObj);
debug(`${root} done (skipped)`, "ts", 0);
}
}
// inject
let hasOperations = false;
for (const injectedType of ctx.injectFooter) {
if (
!hasOperations &&
(injectedType as InterfaceDeclaration)?.name?.escapedText === "operations"
) {
hasOperations = true;
}
type.push(injectedType);
}
if (!hasOperations) {
// if no operations created, inject empty operations type
type.push(
ts.factory.createTypeAliasDeclaration(
/* modifiers */ tsModifiers({ export: true }),
/* name */ "operations",
/* typeParameters */ undefined,
/* type */ tsRecord(STRING, NEVER),
),
);
}
return type;
}