@ensi-platform/typescript-openapi-generator
Version:
A swagger client generator for typescript
547 lines (544 loc) • 17.9 kB
JavaScript
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
};