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