import { URL } from "node:url"; import load, { resolveSchema, VIRTUAL_JSON_URL } from "./load.js"; import { transformSchema } from "./transform/index.js"; import transformMediaTypeObject from "./transform/media-type-object.js"; import transformOperationObject from "./transform/operation-object.js"; import transformParameterObject from "./transform/parameter-object.js"; import transformParameterObjectArray from "./transform/parameter-object-array.js"; import transformRequestBodyObject from "./transform/request-body-object.js"; import transformResponseObject from "./transform/response-object.js"; import transformSchemaObject from "./transform/schema-object.js"; import transformSchemaObjectMap from "./transform/schema-object-map.js"; import { error, escObjKey, getDefaultFetch, getEntries, getSchemaObjectComment, indent } from "./utils.js"; export * from "./types.js"; const EMPTY_OBJECT_RE = /^\s*\{?\s*\}?\s*$/; export const COMMENT_HEADER = `/** * This file was auto-generated by openapi-typescript. * Do not make direct changes to the file. */ `; async function openapiTS(schema, options = {}) { const ctx = { additionalProperties: options.additionalProperties ?? false, alphabetize: options.alphabetize ?? false, cwd: options.cwd ?? new URL(`file://${process.cwd()}/`), defaultNonNullable: options.defaultNonNullable ?? false, discriminators: {}, transform: typeof options.transform === "function" ? options.transform : undefined, postTransform: typeof options.postTransform === "function" ? options.postTransform : undefined, immutableTypes: options.immutableTypes ?? false, emptyObjectsUnknown: options.emptyObjectsUnknown ?? false, indentLv: 0, operations: {}, pathParamsAsTypes: options.pathParamsAsTypes ?? false, parameters: {}, silent: options.silent ?? false, supportArrayLength: options.supportArrayLength ?? false, excludeDeprecated: options.excludeDeprecated ?? false, }; const allSchemas = {}; const schemaURL = typeof schema === "string" ? resolveSchema(schema) : schema; let rootURL = schemaURL; const isInlineSchema = typeof schema !== "string" && schema instanceof URL === false; if (isInlineSchema) { if (ctx.cwd) { if (ctx.cwd instanceof URL) { rootURL = ctx.cwd; } else if (typeof ctx.cwd === "string") { rootURL = new URL(ctx.cwd, `file://${process.cwd()}/`); } rootURL = new URL("root.yaml", rootURL); } else { rootURL = new URL(VIRTUAL_JSON_URL); } } await load(schemaURL, { ...ctx, auth: options.auth, schemas: allSchemas, rootURL, urlCache: new Set(), httpHeaders: options.httpHeaders, httpMethod: options.httpMethod, fetch: options.fetch ?? getDefaultFetch(), }); for (const k of Object.keys(allSchemas)) { const subschema = allSchemas[k]; if (typeof subschema.schema.swagger === "string") { error("Swagger 2.0 and older no longer supported. Please use v5."); process.exit(1); } if (subschema.hint === "OpenAPI3" && typeof subschema.schema.openapi === "string") { if (parseInt(subschema.schema.openapi) !== 3) { error(`Unsupported OpenAPI version "${subschema.schema.openapi}". Only 3.x is supported.`); process.exit(1); } } } const output = []; if ("commentHeader" in options) { if (options.commentHeader) output.push(options.commentHeader); } else { output.push(COMMENT_HEADER); } if (options.inject) output.push(options.inject); const rootTypes = transformSchema(allSchemas["."].schema, ctx); for (const k of Object.keys(rootTypes)) { if (rootTypes[k] && !EMPTY_OBJECT_RE.test(rootTypes[k])) { output.push(options.exportType ? `export type ${k} = ${rootTypes[k]};` : `export interface ${k} ${rootTypes[k]}`, ""); } else { output.push(`export type ${k} = Record;`, ""); } delete rootTypes[k]; delete allSchemas["."]; } const externalKeys = Object.keys(allSchemas); if (externalKeys.length) { let indentLv = 0; output.push(options.exportType ? "export type external = {" : "export interface external {"); externalKeys.sort((a, b) => a.localeCompare(b, "en", { numeric: true })); indentLv++; for (const subschemaID of externalKeys) { const subschema = allSchemas[subschemaID]; const key = escObjKey(subschemaID); const path = `${subschemaID}#`; let subschemaOutput = ""; let comment; switch (subschema.hint) { case "OpenAPI3": { const subschemaTypes = transformSchema(subschema.schema, { ...ctx, indentLv: indentLv + 1 }); if (!Object.keys(subschemaTypes).length) break; output.push(indent(`${key}: {`, indentLv)); indentLv++; for (const [k, v] of getEntries(subschemaTypes, options.alphabetize, options.excludeDeprecated)) { if (EMPTY_OBJECT_RE.test(v)) output.push(indent(`${escObjKey(k)}: Record;`, indentLv)); else output.push(indent(`${escObjKey(k)}: ${v};`, indentLv)); } indentLv--; output.push(indent("};", indentLv)); break; } case "MediaTypeObject": { subschemaOutput = transformMediaTypeObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); break; } case "OperationObject": { comment = getSchemaObjectComment(subschema.schema, indentLv); subschemaOutput = transformOperationObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); break; } case "ParameterObject": { subschemaOutput = transformParameterObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); break; } case "ParameterObject[]": { if (typeof subschema.schema === "object" && ("schema" in subschema.schema || "type" in subschema.schema)) { subschemaOutput = transformSchemaObject(subschema.schema, { path, ctx: { ...ctx, indentLv } }); } else { subschemaOutput += "{\n"; indentLv++; subschemaOutput += transformParameterObjectArray(subschema.schema, { path, ctx: { ...ctx, indentLv } }); subschemaOutput += "\n"; indentLv--; subschemaOutput += indent("};", indentLv); } break; } case "RequestBodyObject": { subschemaOutput = `${transformRequestBodyObject(subschema.schema, { path, ctx: { ...ctx, indentLv } })};`; break; } case "ResponseObject": { subschemaOutput = `${transformResponseObject(subschema.schema, { path, ctx: { ...ctx, indentLv } })};`; break; } case "SchemaMap": { subschemaOutput = `${transformSchemaObjectMap(subschema.schema, { path, ctx: { ...ctx, indentLv } })};`; break; } case "SchemaObject": { subschemaOutput = `${transformSchemaObject(subschema.schema, { path, ctx: { ...ctx, indentLv } })};`; break; } default: { error(`Could not resolve subschema ${subschemaID}. Unknown type "${subschema.hint}".`); process.exit(1); } } if (subschemaOutput && !EMPTY_OBJECT_RE.test(subschemaOutput)) { if (comment) output.push(indent(comment, indentLv)); output.push(indent(`${key}: ${subschemaOutput}`, indentLv)); } delete allSchemas[subschemaID]; } indentLv--; output.push(indent(`}${options.exportType ? ";" : ""}`, indentLv), ""); } else { output.push(`export type external = Record;`, ""); } if (Object.keys(ctx.operations).length) { output.push(options.exportType ? "export type operations = {" : "export interface operations {", ""); for (const [key, { operationType, comment }] of Object.entries(ctx.operations)) { if (comment) output.push(indent(comment, 1)); output.push(indent(`${escObjKey(key)}: ${operationType};`, 1)); } output.push(`}${options.exportType ? ";" : ""}`, ""); } else { output.push(`export type operations = Record;`, ""); } if (output.join("\n").includes("OneOf")) { output.splice(1, 0, "/** OneOf type helpers */", "type Without = { [P in Exclude]?: never };", "type XOR = (T | U) extends object ? (Without & U) | (Without & T) : T | U;", "type OneOf = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR, ...Rest]> : never;", ""); } if (output.join("\n").includes("WithRequired")) { output.splice(1, 0, "/** WithRequired type helpers */", "type WithRequired = T & { [P in K]-?: T[P] };", ""); } return output.join("\n"); } export default openapiTS; //# sourceMappingURL=index.js.map