UNPKG

@ensi-platform/typescript-openapi-generator

Version:
547 lines (544 loc) 17.9 kB
var M = Object.defineProperty; var T = (i, e, s) => e in i ? M(i, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : i[e] = s; var a = (i, e, s) => T(i, typeof e != "symbol" ? e + "" : e, s); import h, { existsSync as E } from "node:fs"; import m, { resolve as L } from "node:path"; import { generate as k } from "orval"; import { rimraf as C } from "rimraf"; import O from "yaml"; import { writeFile as D } from "node:fs/promises"; import { tsImport as V } from "ts-import"; import { parse as W } from "@stoplight/yaml"; import I from "lodash.clonedeep"; import v from "node:readline"; const j = class j { static async create() { const e = "./typescript-openapi-generator.ts"; if (E(e)) throw new Error("The file already exists"); await D(e, j.DEFAULT); } async load() { const e = L(process.cwd(), "./typescript-openapi-generator.ts"); try { const s = await V.compile(e); if (!s) throw new Error(); return s.default; } catch (s) { console.error("Cannot find module typescript-openapi-generator.ts", s); } } }; a(j, "DEFAULT", `import { type ITypescriptOpenapiGeneratorConfig } from '@ensi-platform/typescript-openapi-generator'; const config: ITypescriptOpenapiGeneratorConfig = { cache: [ { input: '', output: '', }, ], orval: {}, }; export default config;`); let S = j; const u = "#/", b = (i) => { try { const e = h.readFileSync(i, "utf8"); return O.parse(e); } catch (e) { return console.error(`Error reading file: ${i} - ${e}`), null; } }, $ = (i) => { if (i.length === 0) return ""; const e = i.at(-1) || "", s = /\.[\dA-Za-z]+/.test(e); return i.map((n, c) => c === i.length - 1 ? n : s ? n.replace(/\/[^/]+(\.yaml)?(#.*)?$/, "/") : n.split("#")[0]).join("/").split("/").reduce((n, c) => { if (!c) return n; if (c === ".") return n.at(-1) || n.push(c), n; if (c === "..") return n.pop(), n; const p = n.at(-1); return c === "#" ? (n[n.length - 1] = `${p}#`, n) : (n.push(c), n); }, []).join("/"); }, z = (i) => i.split("_").map((t) => t.charAt(0).toUpperCase() + t.slice(1).toLowerCase()).join(""), P = (i, e) => e.reduce((t, r) => t[r] || {}, i), U = (i) => { const e = i.split("/").at(-1); if (!e) return; const s = e.replace(".yaml", ""); return z(s); }; class J { constructor(e) { a(this, "pathToIndex"); a(this, "nodes", /* @__PURE__ */ new Map()); a(this, "nodesObj", /* @__PURE__ */ new Map()); a(this, "getValidSchemaObject", (e) => { if (e && typeof e == "object") return e; }); a(this, "hasDuplicate", (e, s) => { var t; return this.nodes.has(e) ? ((t = this.nodes.get(e)) == null || t.push(s), !0) : (this.nodes.set(e, [s]), !1); }); a(this, "fileProcessing", (e, s) => { const t = m.join($([s, e])); if (this.nodesObj.has(t)) return; const [r, o] = t.split(u), n = b(r); if (!n) return; const c = (o != null && o.includes("/") ? (o.split("/") || []).at(-1) : o) || U(r); if (!c) return; if (this.hasDuplicate(c, t)) for (const [f, l] of this.nodes.get(c).entries()) this.nodesObj.set(l, { name: c, newName: `${c}${f}` }); else this.nodesObj.set(t, { name: c, newName: "" }); return { file: n, filePath: r }; }); a(this, "findRefs", (e, s) => { if (e.$ref) { const r = this.fileProcessing(e.$ref, s); r && this.findRefs(r.file, r.filePath); return; } const t = Object.keys(e); for (const r of t) { const o = e[r], n = this.getValidSchemaObject(o); if (!n) continue; if (Array.isArray(n)) { for (const p of n) { const f = this.getValidSchemaObject(p); f && (Array.isArray(f) || this.findRefs(f, s)); } continue; } const c = n.$ref; if (c) { const p = this.fileProcessing(c, s); p && this.findRefs(p.file, p.filePath); continue; } this.findRefs(n, s); } }); a(this, "resolve", () => { const e = h.readFileSync(this.pathToIndex, "utf8"), s = O.parse(e); this.findRefs(s, this.pathToIndex); const t = /* @__PURE__ */ new Map(); for (const [r, o] of this.nodesObj.entries()) o.newName && t.set(r, o); return this.nodes.clear(), this.nodesObj.clear(), t; }); this.pathToIndex = e; } } let A = []; const N = (i, e) => { A.push({ message: i, consoleFn: e }); }, _ = () => { for (const { message: i, consoleFn: e } of A.reverse()) e(i); A = []; }, G = (i) => N(i, console.error), B = (i) => N(i, console.warn), F = { array: { type: "array", items: { oneOf: [ { type: "string" }, { type: "integer" } ] } }, object: { type: "object", additionalProperties: { type: "string" } }, parameter: { schema: { type: "string" } } }, H = (i) => i.type === "array" && !i.items ? { ...i, ...I(F.array) } : i.type === "object" && !i.properties ? { ...i, ...I(F.object) } : !i.type && i.in && !i.$ref && !i.schema ? { ...i, ...I(F.parameter) } : i; class Y { constructor(e, s, { parseItem: t }) { a(this, "cacheDir"); a(this, "indexUrl"); a(this, "visitedUrls", /* @__PURE__ */ new Set()); a(this, "downloadedUrls", /* @__PURE__ */ new Set()); a(this, "allUrls", /* @__PURE__ */ new Set()); a(this, "parseItem"); a(this, "transformItem", (e) => { if (!e) return e; if (typeof e == "object") { if (Array.isArray(e)) return e.map((t) => this.transformItem(t)); const s = this.parseItem(e); return Object.keys(s).reduce((t, r) => { const o = s[r]; return typeof o != "object" || !o ? { ...t, [r]: o } : Array.isArray(o) ? { ...t, [r]: o.map((n) => this.transformItem(n)) } : o.type ? { ...t, [r]: this.transformItem(this.parseItem(o)) } : { ...t, [r]: this.transformItem(o) }; }, {}); } return e; }); a(this, "parseSchema", (e) => ({ ...Object.entries(e).reduce( (s, [t, r]) => { const o = this.transformItem(r); return o && (s[t] = o), s; }, {} ) })); a(this, "loadYaml", async (e) => { const s = await fetch(e); if (!s.ok) throw new Error(`Failed to fetch ${e}: ${s.status}`); return await s.text(); }); a(this, "updateFilePath", (e, s) => { if (e.startsWith("/")) { const t = m.basename(e); return `${s}/${t}`; } if (!e.startsWith("http")) return new URL(e, s).toString(); }); a(this, "getFilePath", (e) => { try { const s = new URL(e).pathname, t = m.join(this.cacheDir, s), r = m.dirname(t); return h.existsSync(r) || h.mkdirSync(r, { recursive: !0 }), t; } catch { } }); a(this, "savingFile", (e, s) => { try { const t = this.getFilePath(s); if (!t) throw new Error(`File ${s} was't saving`); const r = W(e), o = this.parseSchema(r), n = O.stringify(o); return h.writeFileSync(t, n), o; } catch (t) { B(t.message); } }); a(this, "preprocessSchema", (e, s) => { const t = JSON.parse(JSON.stringify(e)), r = (o, n) => { if (typeof o == "object" && o !== null) { if (Array.isArray(o)) { for (const c of o) r(c, n); return; } for (const c of Object.keys(o)) { const p = o[c]; if (c === "$ref" && typeof p == "string") { const f = this.updateFilePath(p, n); if (!f) continue; o[c] = f; continue; } typeof p == "object" && r(p, n); } } }; return r(t, s), t; }); a(this, "addAllUrls", (e) => { for (const s of e) this.allUrls.add(s); }); // Traverse the schema to find $ref references a(this, "getRefsFromFile", (e, s) => { const t = []; if (typeof e == "object" && e !== null) { if (Array.isArray(e)) { for (const r of e) t.push(...this.getRefsFromFile(r, s)); return t; } for (const r of Object.keys(e)) { const o = e[r]; if (r === "$ref" && typeof o == "string") { const n = new URL(o).pathname, c = m.join(this.cacheDir, n); h.existsSync(c) || t.push(o.split(u)[0]); continue; } typeof o == "object" && t.push(...this.getRefsFromFile(o, s)); } } return t; }); a(this, "loadSchema", async (e, s) => { const t = e.split(u)[0]; if (!this.visitedUrls.has(t)) { this.visitedUrls.add(t); try { const r = await this.loadYaml(t), o = this.savingFile(r, t); if (this.downloadedUrls.add(e), !o) return; const n = new URL(t), c = n.pathname.split("/").slice(0, -1), p = `${n.origin}${c.join("/")}/`, f = this.preprocessSchema(o, p), l = this.getRefsFromFile(f, this.cacheDir); l.length > 0 && (this.addAllUrls(l), s == null || s({ downloadedUrls: this.downloadedUrls, allUrls: this.allUrls }), await Promise.all(l.map((d) => this.loadSchema(d, s)))); } catch (r) { G(`⚠️ Error processing URL ${t}: ${r.message}`); } } }); a(this, "download", async ({ getFilesData: e }) => { await this.loadSchema(this.indexUrl, e); }); this.indexUrl = e, this.cacheDir = s, this.parseItem = t || H; } } class Z { constructor(e, s) { a(this, "pathToIndex"); a(this, "paths", /* @__PURE__ */ new Map()); a(this, "componentSchemas", /* @__PURE__ */ new Map()); a(this, "componentResponses", /* @__PURE__ */ new Map()); a(this, "componentParameters", /* @__PURE__ */ new Map()); a(this, "duplicateMap", /* @__PURE__ */ new Map()); a(this, "getComponentObj", (e, s) => { let t; return e.in ? t = "parameters" : e.content ? t = "responses" : Object.keys(e).length > 0 && (t = "schemas"), t ? { type: t, path: `${u}components/${t}/${s}` } : null; }); a(this, "setComponentToMap", (e, s, t) => { s === "parameters" && this.componentParameters.set(t, e), s === "schemas" && this.componentSchemas.set(t, e), s === "responses" && this.componentResponses.set(t, e); }); a(this, "getComponentForIndex", (e) => { let s, t; if (this.componentParameters.has(e) && (s = this.componentParameters, t = "parameters"), this.componentSchemas.has(e) && (s = this.componentSchemas, t = "schemas"), this.componentResponses.has(e) && (s = this.componentResponses, t = "responses"), s && t) { const r = s.get(e); return { component: r, path: `${u}components/${t}/${Object.keys(r)[0]}` }; } }); a(this, "getValidSchemaObject", (e) => { if (e && typeof e == "object") return e; }); a(this, "resolveReferenceObject", (e, s) => { var r; const t = e.$ref; if (t) { const o = e, n = m.join($([s, t])), [c, p] = n.split(u); if (n.includes(this.pathToIndex)) { o.$ref = `${u}${p}`; return; } const f = this.getComponentForIndex(n); if (f != null && f.component) { o.$ref = f.path; return; } const l = b(c); if (!l) return; const d = (r = this.duplicateMap.get(n)) == null ? void 0 : r.newName; if (p) { const y = p.includes("/"), w = y ? P(l, p.split("/")) : l[p], R = d || (y ? p.split("/").at(-1) : p), x = this.getComponentObj(w, R); x && this.setSchemaObject({ referenceObj: o, componentObj: x, name: R, filePath: c, filePathWithNode: n, obj: w }); } else { const y = d || U(c); if (!y) return; const w = this.getComponentObj(l, y); w && this.setSchemaObject({ referenceObj: o, componentObj: w, name: y, filePath: c, filePathWithNode: n, obj: l }); } return; } this.resolveSchemaAnyObject(e, s); }); a(this, "resolveSchemaAnyObject", (e, s) => { if (e.$ref) { this.resolveReferenceObject(e, s); return; } const t = Object.keys(e); for (const r of t) { const o = e[r], n = this.getValidSchemaObject(o); if (n) { if (Array.isArray(n)) { for (const c of n) { const p = this.getValidSchemaObject(c); p && (Array.isArray(p) || this.resolveReferenceObject(p, s)); } continue; } this.resolveReferenceObject(n, s); } } }); a(this, "resolve", async () => { var n, c, p; const e = h.readFileSync(this.pathToIndex, "utf8"), s = O.parse(e), { paths: t, ...r } = s; return t && (this.resolveSchemaPaths(t), this.resolveObjectsInSchemaPaths(t)), this.resolveSchemaAnyObject(r, this.pathToIndex), { ...s, components: { ...s.components, responses: { ...(n = s.components) == null ? void 0 : n.responses, ...this.getObjectFromMap(this.componentResponses) }, parameters: { ...(c = s.components) == null ? void 0 : c.parameters, ...this.getObjectFromMap(this.componentParameters) }, schemas: { ...(p = s.components) == null ? void 0 : p.schemas, ...this.getObjectFromMap(this.componentSchemas) } } }; }); this.pathToIndex = e, this.duplicateMap = s; } getObjectFromMap(e) { return [...e.values()].reduce((t, r) => ({ ...t, ...r }), {}); } setSchemaObject({ referenceObj: e, componentObj: s, obj: t, name: r, filePath: o, filePathWithNode: n }) { e.$ref = s.path; const c = { [r]: t }; this.setComponentToMap(c, s.type, n), this.resolveSchemaAnyObject(t, o); } resolveSchemaPaths(e) { const s = Object.keys(e); for (const t of s) { const r = e[t]; if (!r.$ref) continue; const o = r.$ref, n = m.join($([this.pathToIndex, o])), [c, p] = n.split(u), f = b(c); if (f) if (this.paths.set(t, c), p) { const d = p.includes("/") ? P(f, p.split("/")) : f[p]; delete e[t].$ref, typeof d == "object" && (e[t] = { ...e[t], ...d }); } else delete e[t].$ref, e[t] = { ...e[t], ...f }; } } resolveObjectsInSchemaPaths(e) { const s = Object.keys(e); for (const t of s) { const r = this.paths.get(t); if (!r) continue; const o = e[t]; this.resolveSchemaAnyObject(o, r); } } } class q { constructor({ startInfo: e, finishInfo: s, processInfo: t, error: r }) { a(this, "processInfo"); a(this, "startInfo"); a(this, "finishInfo"); a(this, "error"); a(this, "start", () => { console.info(this.startInfo), console.info(this.processInfo + "..."); }); a(this, "finish", () => { v.moveCursor(process.stdout, 0, -1), v.clearLine(process.stdout, 0), console.info("\x1B[32m%s\x1B[0m", this.finishInfo), v.moveCursor(process.stdout, 0, 1); }); a(this, "processing", async (e) => { try { this.start(); const s = await e(); return this.finish(), s; } catch { throw new Error(this.error); } }); this.startInfo = e, this.processInfo = t, this.finishInfo = s, this.error = r; } reinit({ startInfo: e, finishInfo: s, processInfo: t, error: r }) { this.startInfo = e, this.processInfo = t, this.finishInfo = s, r && (this.error = r); } } const g = "./cache", K = "./cache/resolved-schema.yaml", Q = async (i, e, { loaderOptions: s = {} }) => { try { h.existsSync(g) && await C(g), await h.mkdirSync(g, { recursive: !0 }); const t = new q({ startInfo: "Files loading has started", processInfo: "Loading files", finishInfo: "🎉 Files loading completed successfully", error: "Generation error, process stopped" }), r = new Y(i.input, g, s); await t.processing(async () => r.download({})), _(); const o = new URL(i.input).pathname, n = m.join(g, o); t.reinit({ startInfo: "Files resolved has started", processInfo: "Resolving files", finishInfo: "🎉 Files resolve completed successfully" }); const c = new J(n), p = await t.processing(async () => { const l = c.resolve(); return await new Z(n, l).resolve(); }), f = O.stringify(p); await h.writeFileSync(K, f), t.reinit({ startInfo: "Files generation has started", processInfo: "Generationing files", finishInfo: "🎉 Files generation completed successfully" }), await t.processing( () => k({ ...e, output: { ...e.output, target: i.output, schemas: m.join(i.output, "./models") }, input: { target: "./cache/resolved-schema.yaml" } }) ), h.existsSync(g) && await C(g); } catch (t) { console.error(t); } }, fe = async () => { const e = await new S().load(); if (e != null && e.cache) for (const s of e.cache) await Q(s, e.orval, { loaderOptions: e.loaderOptions }); }, le = async () => { await S.create(), console.log("\x1B[32m%s\x1B[0m", "✔️ Configuration file created typescript-openapi-generator.ts"); }; export { fe as generate, le as init };