apigen-ts 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_template.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/{main-l0LIDQ3K.mjs → main-B8jUVaQq.js} +91 -14
- package/dist/main.cjs +570 -11
- package/dist/main.d.cts +4 -0
- package/dist/main.d.mts +4 -0
- package/dist/main.js +1 -1
- package/dist/main.mjs +572 -10
- package/package.json +16 -18
- package/readme.md +103 -83
- package/dist/cli.cjs +0 -18
- package/dist/cli.mjs +0 -16
- package/dist/main-C0qK6dZX.cjs +0 -580
- package/dist/main-l0LIDQ3K.js +0 -576
package/dist/main-C0qK6dZX.cjs
DELETED
|
@@ -1,580 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var fs = require('fs/promises');
|
|
4
|
-
var path$1 = require('path');
|
|
5
|
-
var url = require('url');
|
|
6
|
-
var cleye = require('cleye');
|
|
7
|
-
var redocly = require('@redocly/openapi-core');
|
|
8
|
-
var arrayUtilsTs = require('array-utils-ts');
|
|
9
|
-
var lodashEs = require('lodash-es');
|
|
10
|
-
var swagger2openapi = require('swagger2openapi');
|
|
11
|
-
var ts = require('typescript');
|
|
12
|
-
var path = require('node:path');
|
|
13
|
-
|
|
14
|
-
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
15
|
-
var name = "apigen-ts";
|
|
16
|
-
var version = "1.2.0";
|
|
17
|
-
|
|
18
|
-
const initCtx = (config) => {
|
|
19
|
-
return {
|
|
20
|
-
source: "",
|
|
21
|
-
output: "",
|
|
22
|
-
name: "ApiClient",
|
|
23
|
-
doc: { openapi: "3.1.0" },
|
|
24
|
-
parseDates: false,
|
|
25
|
-
inlineEnums: false,
|
|
26
|
-
headers: {},
|
|
27
|
-
...config,
|
|
28
|
-
logTag: "",
|
|
29
|
-
usedNames: /* @__PURE__ */ new Set()
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
const parseHeaders = (items) => {
|
|
33
|
-
const headers = {};
|
|
34
|
-
for (const item of items) {
|
|
35
|
-
const [key, val] = item.split(":");
|
|
36
|
-
if (key && val) headers[key.trim()] = val.trim();
|
|
37
|
-
}
|
|
38
|
-
return headers;
|
|
39
|
-
};
|
|
40
|
-
const getCliConfig = () => {
|
|
41
|
-
const argv = cleye.cli({
|
|
42
|
-
name,
|
|
43
|
-
version,
|
|
44
|
-
parameters: ["<source>", "[output]"],
|
|
45
|
-
flags: {
|
|
46
|
-
name: {
|
|
47
|
-
type: String,
|
|
48
|
-
description: "API class name to export",
|
|
49
|
-
default: "ApiClient"
|
|
50
|
-
},
|
|
51
|
-
parseDates: {
|
|
52
|
-
type: Boolean,
|
|
53
|
-
description: "Parse dates as Date objects",
|
|
54
|
-
default: false
|
|
55
|
-
},
|
|
56
|
-
inlineEnums: {
|
|
57
|
-
type: Boolean,
|
|
58
|
-
description: "Use inline enums instead of enum types",
|
|
59
|
-
default: false
|
|
60
|
-
},
|
|
61
|
-
header: {
|
|
62
|
-
type: [String],
|
|
63
|
-
alias: "H",
|
|
64
|
-
description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
|
|
65
|
-
default: []
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
const config = {
|
|
70
|
-
source: argv._.source,
|
|
71
|
-
output: argv._.output ?? null,
|
|
72
|
-
name: argv.flags.name,
|
|
73
|
-
parseDates: argv.flags.parseDates,
|
|
74
|
-
inlineEnums: argv.flags.inlineEnums,
|
|
75
|
-
headers: parseHeaders(argv.flags.header)
|
|
76
|
-
};
|
|
77
|
-
return config;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const unref = (ctx, s) => {
|
|
81
|
-
if (!s) return void 0;
|
|
82
|
-
if ("$ref" in s && s.$ref) {
|
|
83
|
-
const parts = s.$ref.replace("#/", "").split("/");
|
|
84
|
-
const obj = parts.reduce(
|
|
85
|
-
// openapi encodes "/" in key as "~1"
|
|
86
|
-
(acc, x) => lodashEs.get(acc, x, lodashEs.get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
|
|
87
|
-
ctx.doc
|
|
88
|
-
);
|
|
89
|
-
if (obj) return obj;
|
|
90
|
-
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
91
|
-
return void 0;
|
|
92
|
-
}
|
|
93
|
-
return s;
|
|
94
|
-
};
|
|
95
|
-
const getReqSchema = (ctx, config) => {
|
|
96
|
-
const req = unref(ctx, config.requestBody);
|
|
97
|
-
if (!req) return void 0;
|
|
98
|
-
const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
|
|
99
|
-
if (cts.length === 0) return void 0;
|
|
100
|
-
const pretenders = [
|
|
101
|
-
"application/json",
|
|
102
|
-
"text/",
|
|
103
|
-
"multipart/form-data",
|
|
104
|
-
"application/x-www-form-urlencoded"
|
|
105
|
-
];
|
|
106
|
-
for (const p of pretenders) {
|
|
107
|
-
const ct = cts.find((x) => x[0].startsWith(p));
|
|
108
|
-
if (ct) return ct;
|
|
109
|
-
}
|
|
110
|
-
cts.map((x) => x[0]);
|
|
111
|
-
return void 0;
|
|
112
|
-
};
|
|
113
|
-
const getRepSchema = (ctx, config) => {
|
|
114
|
-
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => lodashEs.get(config, ["responses", x, "content"]));
|
|
115
|
-
const cts = Object.entries(lodashEs.get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
|
|
116
|
-
if (cts.length === 0) return void 0;
|
|
117
|
-
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
118
|
-
if (ctJson) return ctJson[1].schema;
|
|
119
|
-
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
120
|
-
if (ctText) return { type: "string" };
|
|
121
|
-
cts.map((x) => x[0]).join(", ");
|
|
122
|
-
return void 0;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const f$2 = ts.factory;
|
|
126
|
-
const Keywords = /* @__PURE__ */ new Set([
|
|
127
|
-
"break",
|
|
128
|
-
"case",
|
|
129
|
-
"catch",
|
|
130
|
-
"class",
|
|
131
|
-
"const",
|
|
132
|
-
"continue",
|
|
133
|
-
"debugger",
|
|
134
|
-
"default",
|
|
135
|
-
"delete",
|
|
136
|
-
"do",
|
|
137
|
-
"else",
|
|
138
|
-
"enum",
|
|
139
|
-
"export",
|
|
140
|
-
"extends",
|
|
141
|
-
"false",
|
|
142
|
-
"finally",
|
|
143
|
-
"for",
|
|
144
|
-
"function",
|
|
145
|
-
"if",
|
|
146
|
-
"import",
|
|
147
|
-
"in",
|
|
148
|
-
"instanceof",
|
|
149
|
-
"new",
|
|
150
|
-
"null",
|
|
151
|
-
"return",
|
|
152
|
-
"super",
|
|
153
|
-
"switch",
|
|
154
|
-
"this",
|
|
155
|
-
"throw",
|
|
156
|
-
"true",
|
|
157
|
-
"try",
|
|
158
|
-
"typeof",
|
|
159
|
-
"var",
|
|
160
|
-
"void",
|
|
161
|
-
"while",
|
|
162
|
-
"with",
|
|
163
|
-
"implements",
|
|
164
|
-
"interface",
|
|
165
|
-
"let",
|
|
166
|
-
"package",
|
|
167
|
-
"private",
|
|
168
|
-
"protected",
|
|
169
|
-
"public",
|
|
170
|
-
"static",
|
|
171
|
-
"yield",
|
|
172
|
-
"any",
|
|
173
|
-
"boolean",
|
|
174
|
-
"number",
|
|
175
|
-
"string",
|
|
176
|
-
"symbol",
|
|
177
|
-
// "abstract", "as", "async", "await", "constructor", "declare", "from", "get", "is", "module",
|
|
178
|
-
// "namespace", "of", "require", "set", "type",
|
|
179
|
-
"Record",
|
|
180
|
-
"Partial",
|
|
181
|
-
"Pick",
|
|
182
|
-
"Omit",
|
|
183
|
-
"Exclude",
|
|
184
|
-
"Extract",
|
|
185
|
-
// ts keywords
|
|
186
|
-
"Date",
|
|
187
|
-
"object",
|
|
188
|
-
"Response"
|
|
189
|
-
// ts type names
|
|
190
|
-
]);
|
|
191
|
-
const normalizeIdentifier = (val, asVar = false) => {
|
|
192
|
-
let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
193
|
-
if (name.match(/^\d/)) name = `$${name}`;
|
|
194
|
-
if (asVar && Keywords.has(name)) name = `$${name}`;
|
|
195
|
-
return name;
|
|
196
|
-
};
|
|
197
|
-
const makeInlineEnum = (s) => {
|
|
198
|
-
if (!s.enum) return void 0;
|
|
199
|
-
const values = arrayUtilsTs.filterEmpty(s.enum);
|
|
200
|
-
if (!values.length) return void 0;
|
|
201
|
-
if (!s.type) {
|
|
202
|
-
if (values.every((x) => typeof x === "string")) s.type = "string";
|
|
203
|
-
if (values.every((x) => typeof x === "number")) s.type = "number";
|
|
204
|
-
if (values.every((x) => typeof x === "boolean")) s.type = "boolean";
|
|
205
|
-
}
|
|
206
|
-
if (s.type === "string") {
|
|
207
|
-
const tokens = lodashEs.uniq(values).map((x) => f$2.createStringLiteral(x.toString()));
|
|
208
|
-
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
209
|
-
}
|
|
210
|
-
if (s.type === "number") {
|
|
211
|
-
const tokens = lodashEs.uniq(values).map((x) => f$2.createNumericLiteral(x));
|
|
212
|
-
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
213
|
-
}
|
|
214
|
-
if (s.type === "boolean") {
|
|
215
|
-
const tokens = [];
|
|
216
|
-
if (values.includes(true)) tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
|
|
217
|
-
if (values.includes(false)) tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
|
|
218
|
-
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
219
|
-
}
|
|
220
|
-
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
221
|
-
return void 0;
|
|
222
|
-
};
|
|
223
|
-
const makeObject = (ctx, s) => {
|
|
224
|
-
if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
225
|
-
if (s.additionalProperties && !lodashEs.isBoolean(s.additionalProperties)) {
|
|
226
|
-
return f$2.createTypeReferenceNode("Record", [
|
|
227
|
-
f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
228
|
-
makeType(ctx, s.additionalProperties)
|
|
229
|
-
]);
|
|
230
|
-
}
|
|
231
|
-
return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
|
|
232
|
-
};
|
|
233
|
-
const makeLiteralUnion = (ctx, types) => {
|
|
234
|
-
const tokens = types.map((x) => makeType(ctx, { type: x }));
|
|
235
|
-
const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
|
|
236
|
-
if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
237
|
-
return f$2.createUnionTypeNode(tokens);
|
|
238
|
-
};
|
|
239
|
-
const makeType = (ctx, s) => {
|
|
240
|
-
const mk = makeType.bind(null, ctx);
|
|
241
|
-
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
242
|
-
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
243
|
-
if ("$ref" in s && s.$ref) {
|
|
244
|
-
const parts = s.$ref.replace("#/", "").split("/");
|
|
245
|
-
if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
|
|
246
|
-
return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
|
|
247
|
-
}
|
|
248
|
-
const t = unref(ctx, s);
|
|
249
|
-
if (!t) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
|
|
250
|
-
return makeType(ctx, t);
|
|
251
|
-
}
|
|
252
|
-
if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
|
|
253
|
-
if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
|
|
254
|
-
if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
|
|
255
|
-
if ("type" in s && s.type === "integer") s.type = "number";
|
|
256
|
-
if ("enum" in s && s.enum && !Array.isArray(s.type)) {
|
|
257
|
-
const isArray2 = s.type === "array";
|
|
258
|
-
const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
|
|
259
|
-
if (t) return isArray2 ? f$2.createArrayTypeNode(t) : t;
|
|
260
|
-
}
|
|
261
|
-
if ("properties" in s && s.properties) {
|
|
262
|
-
return f$2.createTypeLiteralNode(
|
|
263
|
-
Object.entries(s.properties).map(([k, v]) => {
|
|
264
|
-
const r = s.required ?? [];
|
|
265
|
-
const q = r.includes(k) ? void 0 : f$2.createToken(ts.SyntaxKind.QuestionToken);
|
|
266
|
-
return f$2.createPropertySignature(void 0, f$2.createStringLiteral(k), q, mk(v));
|
|
267
|
-
})
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
if ("type" in s) {
|
|
271
|
-
let t;
|
|
272
|
-
if (s.type === "object") t = makeObject(ctx, s);
|
|
273
|
-
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
274
|
-
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
275
|
-
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
276
|
-
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
277
|
-
else if (lodashEs.isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
278
|
-
else if (s.type === "array" && !lodashEs.isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
|
|
279
|
-
else {
|
|
280
|
-
console.warn(`makeType: unknown type "${s.type}"`);
|
|
281
|
-
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
282
|
-
}
|
|
283
|
-
if (s.type === "string") {
|
|
284
|
-
if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
|
|
285
|
-
if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
|
|
286
|
-
}
|
|
287
|
-
if ("nullable" in s && s.nullable) {
|
|
288
|
-
return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
|
|
289
|
-
}
|
|
290
|
-
return t;
|
|
291
|
-
}
|
|
292
|
-
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
293
|
-
};
|
|
294
|
-
const isStringEnum = (s) => {
|
|
295
|
-
if ("enum" in s && s.enum) {
|
|
296
|
-
return s.enum.every((x) => typeof x === "string");
|
|
297
|
-
}
|
|
298
|
-
return false;
|
|
299
|
-
};
|
|
300
|
-
const makeTypeAlias = (ctx, name, s) => {
|
|
301
|
-
if (isStringEnum(s) && !ctx.inlineEnums) {
|
|
302
|
-
const tokens1 = lodashEs.uniq(s.enum);
|
|
303
|
-
const tokens2 = arrayUtilsTs.filterEmpty(tokens1);
|
|
304
|
-
if (tokens1.length !== tokens2.length) {
|
|
305
|
-
console.warn(`enum ${name} has empty values`, s);
|
|
306
|
-
}
|
|
307
|
-
return f$2.createEnumDeclaration(
|
|
308
|
-
[f$2.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
309
|
-
normalizeIdentifier(name, true),
|
|
310
|
-
tokens2.map(
|
|
311
|
-
(x) => f$2.createEnumMember(lodashEs.upperFirst(normalizeIdentifier(x)), f$2.createStringLiteral(x))
|
|
312
|
-
)
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
return f$2.createTypeAliasDeclaration(
|
|
316
|
-
[f$2.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
317
|
-
f$2.createIdentifier(normalizeIdentifier(name, true)),
|
|
318
|
-
void 0,
|
|
319
|
-
makeType(ctx, s)
|
|
320
|
-
);
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const f$1 = ts.factory;
|
|
324
|
-
const HttpMethods = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
|
|
325
|
-
const normalizeOpName = (val) => {
|
|
326
|
-
const articles = /* @__PURE__ */ new Set(["a", "an", "the"]);
|
|
327
|
-
const tmp = val.replace(/'/, "").replace(/[^a-zA-Z0-9]/g, "_").split("_").filter((x) => x !== "" && !articles.has(x)).map((x) => lodashEs.upperFirst(x));
|
|
328
|
-
tmp[0] = tmp[0].toUpperCase() === tmp[0] ? tmp[0].toLowerCase() : lodashEs.lowerFirst(tmp[0]);
|
|
329
|
-
return tmp.join("");
|
|
330
|
-
};
|
|
331
|
-
const getOpName = (ctx, op) => {
|
|
332
|
-
let ns = normalizeOpName(arrayUtilsTs.filterEmpty(op.tags ?? [])[0] ?? "general");
|
|
333
|
-
let fn = op.operationId ?? null;
|
|
334
|
-
if (!fn) {
|
|
335
|
-
fn = op.path.replace(/^(\/api)?(\/v?\d\.?\d?)?\/(.+)$/, "$3");
|
|
336
|
-
fn = `${op.method}/${fn}`.replace(/\/+/, "/");
|
|
337
|
-
}
|
|
338
|
-
fn = normalizeOpName(fn);
|
|
339
|
-
let nsr = ns.split("").map((x) => `[${x.toUpperCase()}${x.toLowerCase()}]`).join("");
|
|
340
|
-
if (nsr.endsWith("[Ss]")) nsr += "?";
|
|
341
|
-
fn = fn.replace(new RegExp(`^${nsr}([Cc]ontroller|[Ss]ervice)?([A-Z].*)$`), "$2");
|
|
342
|
-
fn = lodashEs.lowerFirst(fn);
|
|
343
|
-
const proposal = [ns, fn];
|
|
344
|
-
if (ctx.resolveName) {
|
|
345
|
-
const res = ctx.resolveName(ctx, op, proposal);
|
|
346
|
-
if (Array.isArray(res) && res.length === 2) return res;
|
|
347
|
-
if (res !== void 0) {
|
|
348
|
-
console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return proposal;
|
|
352
|
-
};
|
|
353
|
-
const prepareUrl = (url, rename) => {
|
|
354
|
-
for (const [k, v] of Object.entries(rename)) url = url.replaceAll(`{${k}}`, "${" + v + "}");
|
|
355
|
-
const parts = url.split("${");
|
|
356
|
-
if (parts.length === 1) return f$1.createStringLiteral(url);
|
|
357
|
-
return f$1.createTemplateExpression(
|
|
358
|
-
f$1.createTemplateHead(parts[0]),
|
|
359
|
-
parts.slice(1).map((x, i) => {
|
|
360
|
-
const [name, ...rest] = x.split("}");
|
|
361
|
-
const right = rest.join("}");
|
|
362
|
-
return f$1.createTemplateSpan(
|
|
363
|
-
f$1.createIdentifier(name),
|
|
364
|
-
// no normalization required
|
|
365
|
-
i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
|
|
366
|
-
);
|
|
367
|
-
})
|
|
368
|
-
);
|
|
369
|
-
};
|
|
370
|
-
const prepareOp = (ctx, cfg, opName) => {
|
|
371
|
-
cfg.parameters = cfg.parameters ?? [];
|
|
372
|
-
const reqSchema = getReqSchema(ctx, cfg);
|
|
373
|
-
const repSchema = getRepSchema(ctx, cfg);
|
|
374
|
-
const allParams = arrayUtilsTs.filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
|
|
375
|
-
const params = lodashEs.uniqBy(allParams.filter((x) => x.in === "path"), "name");
|
|
376
|
-
if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
|
|
377
|
-
const search = allParams.filter((x) => x.in === "query");
|
|
378
|
-
allParams.filter((x) => x.in === "header");
|
|
379
|
-
for (const [name, v] of Object.entries({ search })) {
|
|
380
|
-
if (!v.length) continue;
|
|
381
|
-
const properties = v.reduce((acc, x) => ({ ...acc, [x.name]: x.schema }), {});
|
|
382
|
-
params.push({ name, schema: { type: "object", properties } });
|
|
383
|
-
}
|
|
384
|
-
const urlReplacements = {};
|
|
385
|
-
const fnArgs = params.map((x) => {
|
|
386
|
-
const name = normalizeIdentifier(x.name, true);
|
|
387
|
-
const type = makeType(ctx, x.schema);
|
|
388
|
-
urlReplacements[x.name] = name;
|
|
389
|
-
return f$1.createParameterDeclaration(void 0, void 0, name, void 0, type);
|
|
390
|
-
});
|
|
391
|
-
const cbArgs = arrayUtilsTs.filterNullable([
|
|
392
|
-
search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
|
|
393
|
-
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
394
|
-
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
395
|
-
"headers",
|
|
396
|
-
f$1.createObjectLiteralExpression([
|
|
397
|
-
f$1.createPropertyAssignment(
|
|
398
|
-
f$1.createStringLiteral("content-type"),
|
|
399
|
-
f$1.createStringLiteral(reqSchema[0])
|
|
400
|
-
)
|
|
401
|
-
])
|
|
402
|
-
) : void 0
|
|
403
|
-
]);
|
|
404
|
-
return f$1.createPropertyAssignment(
|
|
405
|
-
f$1.createIdentifier(normalizeIdentifier(opName)),
|
|
406
|
-
f$1.createArrowFunction(
|
|
407
|
-
void 0,
|
|
408
|
-
void 0,
|
|
409
|
-
fnArgs,
|
|
410
|
-
void 0,
|
|
411
|
-
void 0,
|
|
412
|
-
f$1.createBlock([
|
|
413
|
-
f$1.createReturnStatement(
|
|
414
|
-
f$1.createCallExpression(
|
|
415
|
-
f$1.createIdentifier("this.Fetch"),
|
|
416
|
-
[makeType(ctx, repSchema)],
|
|
417
|
-
[
|
|
418
|
-
f$1.createStringLiteral(cfg.method),
|
|
419
|
-
// method
|
|
420
|
-
prepareUrl(cfg.path, urlReplacements),
|
|
421
|
-
// path
|
|
422
|
-
f$1.createObjectLiteralExpression(cbArgs)
|
|
423
|
-
// { query, body, headers }
|
|
424
|
-
]
|
|
425
|
-
)
|
|
426
|
-
)
|
|
427
|
-
])
|
|
428
|
-
)
|
|
429
|
-
);
|
|
430
|
-
};
|
|
431
|
-
const prepareNs = (ctx, name, handlers) => {
|
|
432
|
-
return f$1.createPropertyDeclaration(
|
|
433
|
-
void 0,
|
|
434
|
-
normalizeIdentifier(name),
|
|
435
|
-
void 0,
|
|
436
|
-
void 0,
|
|
437
|
-
f$1.createObjectLiteralExpression(handlers)
|
|
438
|
-
);
|
|
439
|
-
};
|
|
440
|
-
const prepareRoutes = async (ctx) => {
|
|
441
|
-
const routes = {};
|
|
442
|
-
for (const [path, pathConfig] of Object.entries(ctx.doc.paths ?? {})) {
|
|
443
|
-
ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
|
|
444
|
-
if (!lodashEs.isObject(pathConfig)) continue;
|
|
445
|
-
if ("$ref" in pathConfig) {
|
|
446
|
-
console.warn(`${ctx.logTag} $ref should be resolved before (skipping)`);
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
449
|
-
for (const method of HttpMethods) {
|
|
450
|
-
ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
|
|
451
|
-
const config = pathConfig[method];
|
|
452
|
-
if (!config) continue;
|
|
453
|
-
if (pathConfig.parameters) {
|
|
454
|
-
config.parameters = [...config.parameters ?? [], ...pathConfig.parameters];
|
|
455
|
-
}
|
|
456
|
-
const [ns, op] = getOpName(ctx, { ...config, method, path });
|
|
457
|
-
if (!routes[ns]) routes[ns] = [];
|
|
458
|
-
const joined = [ns, op].join(".");
|
|
459
|
-
if (ctx.usedNames.has(joined)) {
|
|
460
|
-
continue;
|
|
461
|
-
} else {
|
|
462
|
-
ctx.usedNames.add(joined);
|
|
463
|
-
}
|
|
464
|
-
try {
|
|
465
|
-
routes[ns].push(prepareOp(ctx, { ...config, method, path }, op));
|
|
466
|
-
} catch (e) {
|
|
467
|
-
console.error(`${ctx.logTag} - ${e}`, config);
|
|
468
|
-
throw e;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
return routes;
|
|
473
|
-
};
|
|
474
|
-
const prepareTypes = async (ctx) => {
|
|
475
|
-
const types = [];
|
|
476
|
-
const typesConfig = lodashEs.sortBy(Object.entries(ctx.doc.components?.schemas ?? {}), ([k]) => k);
|
|
477
|
-
for (const [name, config] of typesConfig) {
|
|
478
|
-
try {
|
|
479
|
-
types.push(makeTypeAlias(ctx, name, config));
|
|
480
|
-
} catch (e) {
|
|
481
|
-
console.error(`${ctx.logTag} - ${e}`, name, config);
|
|
482
|
-
throw e;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return types;
|
|
486
|
-
};
|
|
487
|
-
const generateAst = async (ctx) => {
|
|
488
|
-
const types = await prepareTypes(ctx);
|
|
489
|
-
const routes = await prepareRoutes(ctx);
|
|
490
|
-
const modules = [];
|
|
491
|
-
for (const [k, v] of Object.entries(routes)) {
|
|
492
|
-
modules.push(prepareNs(ctx, k, v));
|
|
493
|
-
}
|
|
494
|
-
return { modules, types };
|
|
495
|
-
};
|
|
496
|
-
const loadSchema = async ({
|
|
497
|
-
url,
|
|
498
|
-
upgrade = true,
|
|
499
|
-
headers = {}
|
|
500
|
-
}) => {
|
|
501
|
-
if (url.startsWith("file://")) url = url.substring(7);
|
|
502
|
-
const { bundle } = await redocly.bundle({
|
|
503
|
-
ref: url,
|
|
504
|
-
config: await redocly.createConfig({}),
|
|
505
|
-
removeUnusedComponents: false,
|
|
506
|
-
externalRefResolver: new redocly.BaseResolver({
|
|
507
|
-
http: {
|
|
508
|
-
headers: Object.entries(headers).map(([name, value]) => {
|
|
509
|
-
return { name, value, matches: "**" };
|
|
510
|
-
})
|
|
511
|
-
}
|
|
512
|
-
})
|
|
513
|
-
});
|
|
514
|
-
if (bundle.parsed.swagger && upgrade) {
|
|
515
|
-
const { openapi } = await swagger2openapi.convertObj(bundle.parsed, { patch: true });
|
|
516
|
-
return openapi;
|
|
517
|
-
}
|
|
518
|
-
return bundle.parsed;
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
const f = ts.factory;
|
|
522
|
-
const addNewLines = (nodes) => {
|
|
523
|
-
const result = [];
|
|
524
|
-
for (const node of nodes) {
|
|
525
|
-
result.push(node);
|
|
526
|
-
result.push(f.createIdentifier("\n"));
|
|
527
|
-
}
|
|
528
|
-
return result;
|
|
529
|
-
};
|
|
530
|
-
const printCode = (nodes) => {
|
|
531
|
-
return ts.createPrinter().printFile(
|
|
532
|
-
f.createSourceFile(
|
|
533
|
-
addNewLines(nodes),
|
|
534
|
-
f.createToken(ts.SyntaxKind.EndOfFileToken),
|
|
535
|
-
ts.NodeFlags.None
|
|
536
|
-
)
|
|
537
|
-
).replaceAll("}, ", "},\n\n");
|
|
538
|
-
};
|
|
539
|
-
const formatCode = async (code) => {
|
|
540
|
-
try {
|
|
541
|
-
const prettier = await import('prettier');
|
|
542
|
-
const options = await prettier.resolveConfig(path.join(process.cwd(), "file"));
|
|
543
|
-
return prettier.format(code, { ...options, parser: "typescript" });
|
|
544
|
-
} catch (e) {
|
|
545
|
-
return code;
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
const apigen = async (config) => {
|
|
550
|
-
const doc = await loadSchema({ url: config.source, headers: config.headers });
|
|
551
|
-
const ctx = initCtx({ ...config, doc });
|
|
552
|
-
const { modules, types } = await generateAst(ctx);
|
|
553
|
-
const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('main-C0qK6dZX.cjs', document.baseURI).href)))), "_template.ts");
|
|
554
|
-
const file = await fs.readFile(filepath, "utf-8");
|
|
555
|
-
let code = [
|
|
556
|
-
`// Auto-generated by https://github.com/vladkens/apigen-ts`,
|
|
557
|
-
`// Source: ${config.source}
|
|
558
|
-
`,
|
|
559
|
-
// remove all comments expect which starts with "// apigen:"
|
|
560
|
-
...file.split("\n").filter((x) => !/\s*\/\/\s(?!apigen:)/.test(x))
|
|
561
|
-
].join("\n");
|
|
562
|
-
if (!ctx.parseDates) {
|
|
563
|
-
code = code.replace(/\s*ISO_FORMAT\s=.+$/gm, "");
|
|
564
|
-
code = code.replace(/PopulateDates.+?\n\s{2}\}/s, "");
|
|
565
|
-
code = code.replace(/this.PopulateDates\((.+)\)/, "$1");
|
|
566
|
-
}
|
|
567
|
-
code = code.replace("// apigen:modules", printCode(modules));
|
|
568
|
-
code = code.replace("// apigen:types", printCode(types));
|
|
569
|
-
code = code.replace("class ApiClient", `class ${ctx.name}`);
|
|
570
|
-
const result = await formatCode(code);
|
|
571
|
-
if (config.output === null) {
|
|
572
|
-
process.stdout.write(result);
|
|
573
|
-
} else {
|
|
574
|
-
await fs.mkdir(path$1.dirname(config.output), { recursive: true });
|
|
575
|
-
await fs.writeFile(config.output, result);
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
|
|
579
|
-
exports.apigen = apigen;
|
|
580
|
-
exports.getCliConfig = getCliConfig;
|