@sebspark/openapi-typegen 5.0.6 → 5.0.8
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/{index.d.ts → index.d.mts} +4 -2
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +893 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +11 -5
- package/dist/index.js +0 -1087
- package/dist/index.js.map +0 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { parse, resolve } from "node:path";
|
|
3
|
+
import { constantCase, pascalCase } from "change-case";
|
|
4
|
+
import * as YAML from "yaml";
|
|
5
|
+
import { format } from "prettier";
|
|
6
|
+
|
|
7
|
+
//#region src/generator/formatter.ts
|
|
8
|
+
const options = {
|
|
9
|
+
parser: "typescript",
|
|
10
|
+
singleQuote: true,
|
|
11
|
+
semi: false,
|
|
12
|
+
trailingComma: "all"
|
|
13
|
+
};
|
|
14
|
+
const format$1 = async (code) => format(code, options);
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/generator/document.ts
|
|
18
|
+
const document = ({ title, description }) => {
|
|
19
|
+
if (title || description) {
|
|
20
|
+
const tokens = [];
|
|
21
|
+
tokens.push("/**");
|
|
22
|
+
if (title) tokens.push(` * ${title}`);
|
|
23
|
+
if (description) tokens.push(` * ${description}`);
|
|
24
|
+
tokens.push(" */\n");
|
|
25
|
+
return tokens.join("\n");
|
|
26
|
+
}
|
|
27
|
+
return "";
|
|
28
|
+
};
|
|
29
|
+
const documentClientPath = (path, responses) => documentPath(path, responses, [param("url", "string")], [param("opts", "RequestOptions", true)]);
|
|
30
|
+
const documentServerPath = (path, responses) => documentPath(path, responses);
|
|
31
|
+
const documentPath = (path, responses, argsBefore = [], argsAfter = []) => {
|
|
32
|
+
const tokens = [];
|
|
33
|
+
tokens.push("/**");
|
|
34
|
+
if (path.title) tokens.push(` * ${path.title}`);
|
|
35
|
+
if (path.description) tokens.push(` * ${path.description}`);
|
|
36
|
+
tokens.push(" *");
|
|
37
|
+
tokens.push(...argsBefore);
|
|
38
|
+
if (path.args) tokens.push(...documentArgs(path.args));
|
|
39
|
+
tokens.push(...argsAfter);
|
|
40
|
+
tokens.push(` * @returns {Promise<${responses}>}`);
|
|
41
|
+
tokens.push(" */");
|
|
42
|
+
return tokens.join("\n");
|
|
43
|
+
};
|
|
44
|
+
const documentArgs = (args) => {
|
|
45
|
+
const tokens = [];
|
|
46
|
+
tokens.push(param("args", "Object", argsOptional(args), "The arguments for the request."));
|
|
47
|
+
tokens.push(...requestArgs(args.path, "params", "Path parameters"));
|
|
48
|
+
tokens.push(...requestArgs(args.query, "query", "Query parameters"));
|
|
49
|
+
tokens.push(...requestArgs(args.header, "headers", "Headers"));
|
|
50
|
+
tokens.push(...requestArgs(args.body, "body", "Request body"));
|
|
51
|
+
return tokens;
|
|
52
|
+
};
|
|
53
|
+
const buildPath = (path, property) => rxProperVariable.test(property) ? `${path}.${property}` : `${path}["${property}"]`;
|
|
54
|
+
const requestArgs = (args, name, title) => {
|
|
55
|
+
if (!args) return [];
|
|
56
|
+
const tokens = [];
|
|
57
|
+
const type = (args.allOf || []).map((e) => e.type).join(AND) || "Object";
|
|
58
|
+
tokens.push(param(buildPath("args", name), type, args.optional, `${title} for the request.`));
|
|
59
|
+
const properties = args.properties.flatMap((prop) => requestProperty(buildPath("args", name), prop));
|
|
60
|
+
tokens.push(...properties);
|
|
61
|
+
return tokens;
|
|
62
|
+
};
|
|
63
|
+
const requestProperty = (path, property) => {
|
|
64
|
+
const tokens = [];
|
|
65
|
+
const type = property.type.map((t) => t.type).join(OR);
|
|
66
|
+
tokens.push(param(buildPath(path, property.name), type, property.optional, property.title, property.description));
|
|
67
|
+
return tokens;
|
|
68
|
+
};
|
|
69
|
+
const param = (name, type, optional = false, title = "", description = "") => {
|
|
70
|
+
const tokens = [];
|
|
71
|
+
tokens.push(` * @param {${type}} ${optional ? "[" : ""}${name}${optional ? "]" : ""}`);
|
|
72
|
+
if (optional || title || description) {
|
|
73
|
+
tokens.push(" -");
|
|
74
|
+
if (optional) tokens.push(" Optional.");
|
|
75
|
+
if (title) tokens.push(` ${title}`);
|
|
76
|
+
if (description) tokens.push(` ${description}`);
|
|
77
|
+
}
|
|
78
|
+
return tokens.join("");
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/generator/common.ts
|
|
83
|
+
const OR = " | ";
|
|
84
|
+
const AND = " & ";
|
|
85
|
+
const generateType = (parsed) => {
|
|
86
|
+
let type;
|
|
87
|
+
switch (parsed.type) {
|
|
88
|
+
case "enum":
|
|
89
|
+
type = generateEnum(parsed);
|
|
90
|
+
break;
|
|
91
|
+
case "array":
|
|
92
|
+
type = generateArray(parsed);
|
|
93
|
+
break;
|
|
94
|
+
case "object":
|
|
95
|
+
type = generateObject(parsed);
|
|
96
|
+
break;
|
|
97
|
+
case "record":
|
|
98
|
+
type = generateRecord(parsed);
|
|
99
|
+
break;
|
|
100
|
+
case "unknown":
|
|
101
|
+
type = generateUnknown(parsed);
|
|
102
|
+
break;
|
|
103
|
+
case "Date":
|
|
104
|
+
case "bigint":
|
|
105
|
+
case "boolean":
|
|
106
|
+
case "null":
|
|
107
|
+
case "number":
|
|
108
|
+
case "string":
|
|
109
|
+
case "symbol":
|
|
110
|
+
case "undefined":
|
|
111
|
+
type = generatePrimitive(parsed);
|
|
112
|
+
break;
|
|
113
|
+
default: type = generateCustom(parsed);
|
|
114
|
+
}
|
|
115
|
+
return type.replace(/ [&|] \{\s*\}/g, "");
|
|
116
|
+
};
|
|
117
|
+
const generateProperty = (property) => {
|
|
118
|
+
const types = property.type.map(generateType);
|
|
119
|
+
return `${document(property)}${propertyName(property.name)}${property.optional ? "?" : ""}: ${types.join(OR) || "unknown"}`;
|
|
120
|
+
};
|
|
121
|
+
const preamble = (type) => type.name ? `${document(type)}export type ${typeName(type.name)} = ` : "";
|
|
122
|
+
const rxProperVariable = /^[a-zA-Z_<>$][a-zA-Z0-9_<>$]*$/;
|
|
123
|
+
const isValidName = (name) => {
|
|
124
|
+
const namingConventionRegex = /^([A-Z_]\w*)([a-z_]\w*)(<([a-z_]\w*(,\s*)?)+>)?$/;
|
|
125
|
+
const hasCapitalLetterRegex = /[A-Z]/;
|
|
126
|
+
if (!namingConventionRegex.test(name)) return false;
|
|
127
|
+
if (!hasCapitalLetterRegex.test(name)) return false;
|
|
128
|
+
if (name[0] !== name[0].toUpperCase() && !name.includes("_")) return false;
|
|
129
|
+
return true;
|
|
130
|
+
};
|
|
131
|
+
const typeName = (name) => {
|
|
132
|
+
if (isValidName(name)) return name;
|
|
133
|
+
if (name.includes("<")) return name.replace(/<([^>]+)>/, (_match, genericContent) => `<${typeName(genericContent)}>`);
|
|
134
|
+
const domainStyleTransformed = name.split(".").map((part, index, array) => {
|
|
135
|
+
if (index === array.length - 1) return pascalCase(part);
|
|
136
|
+
return part;
|
|
137
|
+
}).join("_");
|
|
138
|
+
const prefixedIfNumberStart = domainStyleTransformed.match(/^\d/) ? `_${domainStyleTransformed}` : domainStyleTransformed;
|
|
139
|
+
const finalName = prefixedIfNumberStart.includes("_") ? prefixedIfNumberStart : pascalCase(prefixedIfNumberStart);
|
|
140
|
+
if (finalName.includes("_")) {
|
|
141
|
+
const lastUnderscoreIndex = finalName.lastIndexOf("_");
|
|
142
|
+
if (lastUnderscoreIndex !== -1 && lastUnderscoreIndex < finalName.length - 1) return finalName.substring(0, lastUnderscoreIndex + 1) + finalName.charAt(lastUnderscoreIndex + 1).toUpperCase() + finalName.slice(lastUnderscoreIndex + 2);
|
|
143
|
+
return finalName;
|
|
144
|
+
}
|
|
145
|
+
return finalName.charAt(0).toUpperCase() + finalName.slice(1);
|
|
146
|
+
};
|
|
147
|
+
const propertyName = (name) => {
|
|
148
|
+
if (rxProperVariable.test(name.replace(/\./g, "_"))) return name.replace(/\./g, "_");
|
|
149
|
+
return `'${name.replace(/\./g, "_")}'`;
|
|
150
|
+
};
|
|
151
|
+
const extensions = (type) => (type.allOf || []).map(generateType).concat("").join(AND) + (type.oneOf || []).map(generateType).concat("").join(OR);
|
|
152
|
+
const generatePrimitive = (parsed) => `${preamble(parsed)}${parsed.type}`;
|
|
153
|
+
const generateCustom = (parsed) => `${preamble(parsed)}${typeName(parsed.type)}`;
|
|
154
|
+
const generateUnknown = (parsed) => `${preamble(parsed)}unknown`;
|
|
155
|
+
const generateObject = (parsed) => {
|
|
156
|
+
const lines = [];
|
|
157
|
+
lines.push(`${preamble(parsed)}${extensions(parsed)}{`);
|
|
158
|
+
lines.push(...parsed.properties.map(generateProperty));
|
|
159
|
+
lines.push("}");
|
|
160
|
+
if (parsed.discriminator && parsed.name) lines.push(generateDiscriminator(parsed.discriminator, parsed.name));
|
|
161
|
+
return lines.join("\n");
|
|
162
|
+
};
|
|
163
|
+
const generateRecord = (parsed) => {
|
|
164
|
+
return `Record<string, ${parsed.items.type === "undefined" ? "unknown" : generateType(parsed.items)}>`;
|
|
165
|
+
};
|
|
166
|
+
const generateDiscriminator = (discriminator, name) => {
|
|
167
|
+
const lines = [""];
|
|
168
|
+
lines.push(`export type ${name}Discriminator = {`);
|
|
169
|
+
for (const [key, type] of Object.entries(discriminator.mapping)) lines.push(`${key}: ${type.type}`);
|
|
170
|
+
lines.push("}");
|
|
171
|
+
return lines.join("\n");
|
|
172
|
+
};
|
|
173
|
+
const generateArray = (parsed) => {
|
|
174
|
+
const lines = [];
|
|
175
|
+
let items = generateType(parsed.items);
|
|
176
|
+
if (parsed.items.type === "enum" || "oneOf" in parsed.items) items = `(${items})`;
|
|
177
|
+
lines.push(`${preamble(parsed)}${items}[]`);
|
|
178
|
+
return lines.join("\n");
|
|
179
|
+
};
|
|
180
|
+
const generateEnum = (parsed) => {
|
|
181
|
+
if (parsed.name) {
|
|
182
|
+
const values = parsed.values.map(serializeValue).join(", ");
|
|
183
|
+
const valuesName = constantCase(`${parsed.name}_VALUES`);
|
|
184
|
+
return [`export const ${valuesName} = [${values}] as const`, `${preamble(parsed)}typeof ${valuesName}[number]`].join("\n");
|
|
185
|
+
}
|
|
186
|
+
return `${preamble(parsed)}${parsed.values.map(serializeValue).join(OR)}`;
|
|
187
|
+
};
|
|
188
|
+
const generateHeader = (header) => {
|
|
189
|
+
return `${preamble(header)}{ ${propertyName(header.name)}${header.optional ? "?" : ""}: ${generateType(header.type)} }`;
|
|
190
|
+
};
|
|
191
|
+
const generateResponseBody = (type, optional = true) => {
|
|
192
|
+
const customType = type.type;
|
|
193
|
+
if (customType) return typeName(customType);
|
|
194
|
+
const body = type;
|
|
195
|
+
if (!body.data && !body.headers) return "undefined";
|
|
196
|
+
const tokens = [];
|
|
197
|
+
tokens.push(preamble(body));
|
|
198
|
+
tokens.push("APIResponse<");
|
|
199
|
+
tokens.push(body.data ? generateType(serialized(body.data, optional)) : "undefined");
|
|
200
|
+
if (body.headers) {
|
|
201
|
+
tokens.push(", ");
|
|
202
|
+
tokens.push(body.headers ? generateHeaders(body.headers) : "undefined");
|
|
203
|
+
}
|
|
204
|
+
tokens.push(">");
|
|
205
|
+
return tokens.join("");
|
|
206
|
+
};
|
|
207
|
+
const serialized = (orig, optional = true) => {
|
|
208
|
+
switch (orig.type) {
|
|
209
|
+
case "bigint":
|
|
210
|
+
case "boolean":
|
|
211
|
+
case "enum":
|
|
212
|
+
case "null":
|
|
213
|
+
case "number":
|
|
214
|
+
case "string":
|
|
215
|
+
case "symbol":
|
|
216
|
+
case "undefined": return orig;
|
|
217
|
+
case "Date": return {
|
|
218
|
+
...orig,
|
|
219
|
+
type: "string"
|
|
220
|
+
};
|
|
221
|
+
case "array": return {
|
|
222
|
+
...orig,
|
|
223
|
+
items: serialized(orig.items, optional)
|
|
224
|
+
};
|
|
225
|
+
case "object": return orig;
|
|
226
|
+
default: {
|
|
227
|
+
const wrapper = optional ? "PartiallySerialized" : "Serialized";
|
|
228
|
+
return {
|
|
229
|
+
...orig,
|
|
230
|
+
type: `${wrapper}<${typeName(orig.type)}>`
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const generateHeaders = (headers) => {
|
|
236
|
+
const tokens = [];
|
|
237
|
+
for (const header of headers) tokens.push(`${propertyName(header.name)}${header.optional ? "?" : ""}: ${generateType(header.type)}`);
|
|
238
|
+
return `{${tokens.join(", ")}}`;
|
|
239
|
+
};
|
|
240
|
+
const serializeValue = (value) => {
|
|
241
|
+
if (typeof value === "string") return `'${value}'`;
|
|
242
|
+
return value;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/generator/args.ts
|
|
247
|
+
const generateClientArgs = (args) => generateArgs(args, false);
|
|
248
|
+
const generateServerArgs = (args) => args ? generateArgs(args, true) : "args: Req";
|
|
249
|
+
const parts = [
|
|
250
|
+
"body",
|
|
251
|
+
"header",
|
|
252
|
+
"path",
|
|
253
|
+
"query"
|
|
254
|
+
];
|
|
255
|
+
const generateArgs = (args, isServer) => {
|
|
256
|
+
if (args) {
|
|
257
|
+
const tokens = [];
|
|
258
|
+
for (const part of parts) {
|
|
259
|
+
const arg = args[part];
|
|
260
|
+
if (arg) {
|
|
261
|
+
const partName = part === "path" ? "params" : part === "header" ? "headers" : part;
|
|
262
|
+
if (partName === "query" && isServer) tokens.push(`${partName}${arg.optional ? "?" : ""}: QueryParams<${generateType(arg)}>`);
|
|
263
|
+
else tokens.push(`${partName}${arg.optional ? "?" : ""}: ${wrapArgs(generateType(arg), isServer && part === "header")}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!tokens.length) return "";
|
|
267
|
+
return `args${argsOptional(args) ? "?" : ""}: ${isServer ? "Req & " : ""}{ ${tokens.join(", ")} }, `;
|
|
268
|
+
}
|
|
269
|
+
return "";
|
|
270
|
+
};
|
|
271
|
+
const wrapArgs = (args, wrap) => {
|
|
272
|
+
if (!wrap) return args;
|
|
273
|
+
return `LowerCaseHeaders<${args}>`;
|
|
274
|
+
};
|
|
275
|
+
const argsOptional = (args) => parts.reduce((o, p) => o && (!args[p] || args[p].optional), true);
|
|
276
|
+
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/generator/client.ts
|
|
279
|
+
const generateClient = (name, paths) => {
|
|
280
|
+
const groupedCalls = {};
|
|
281
|
+
for (const path of paths) {
|
|
282
|
+
if (!groupedCalls[path.method]) groupedCalls[path.method] = [];
|
|
283
|
+
groupedCalls[path.method]?.push(generateCall(path));
|
|
284
|
+
}
|
|
285
|
+
const client = [];
|
|
286
|
+
const methods = Object.keys(groupedCalls).map(serializeValue).join(OR);
|
|
287
|
+
client.push(`export type ${name}Client = Pick<BaseClient, ${methods}> & {`);
|
|
288
|
+
Object.entries(groupedCalls).forEach(([method, calls]) => {
|
|
289
|
+
client.push(`${method}: {`);
|
|
290
|
+
client.push(...calls);
|
|
291
|
+
client.push("}");
|
|
292
|
+
});
|
|
293
|
+
client.push("}");
|
|
294
|
+
return client.join("\n");
|
|
295
|
+
};
|
|
296
|
+
const generateCall = (path) => {
|
|
297
|
+
const responses = generateResponses$1(path);
|
|
298
|
+
return `${documentClientPath(path, responses)}
|
|
299
|
+
(
|
|
300
|
+
url: '${path.url}', ${generateClientArgs(path.args)}opts?: RequestOptions,
|
|
301
|
+
): Promise<${responses}>`;
|
|
302
|
+
};
|
|
303
|
+
const generateResponses$1 = (path) => Object.entries(path.responses).filter(([code]) => Number.parseInt(code, 10) < 400).map(([, type]) => generateResponseBody(type, false)).join(OR);
|
|
304
|
+
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/generator/server.ts
|
|
307
|
+
const generateServer = (name, paths) => {
|
|
308
|
+
const tokens = [];
|
|
309
|
+
tokens.push(`export type ${name}ServerPaths = {`);
|
|
310
|
+
for (const [url, methods] of Object.entries(groupPathsByUrl(paths))) tokens.push(generatePath(url, methods));
|
|
311
|
+
tokens.push("}");
|
|
312
|
+
tokens.push("\n");
|
|
313
|
+
tokens.push(`export type ${name}Server = APIServerDefinition & ${name}ServerPaths`);
|
|
314
|
+
return tokens.join("\n");
|
|
315
|
+
};
|
|
316
|
+
const groupPathsByUrl = (paths) => paths.reduce((group, path) => {
|
|
317
|
+
if (!group[path.url]) group[path.url] = [];
|
|
318
|
+
group[path.url].push(path);
|
|
319
|
+
return group;
|
|
320
|
+
}, {});
|
|
321
|
+
const generatePath = (url, methods) => `'${url}': {
|
|
322
|
+
${methods.map(generateMethod).join("\n")}
|
|
323
|
+
}`;
|
|
324
|
+
const generateMethod = (path) => {
|
|
325
|
+
const responses = generateResponses(path.responses);
|
|
326
|
+
return `${path.method}: {
|
|
327
|
+
${documentServerPath(path, responses)}
|
|
328
|
+
handler: (${generateServerArgs(path.args)}) => Promise<${responses}>
|
|
329
|
+
pre?: GenericRouteHandler | GenericRouteHandler[]
|
|
330
|
+
}`;
|
|
331
|
+
};
|
|
332
|
+
const generateResponses = (responses) => Object.entries(responses).filter(([code]) => Number.parseInt(code, 10) < 500).map(([code, response]) => generateResponse(Number.parseInt(code, 10), response)).join(OR);
|
|
333
|
+
const generateResponse = (code, response) => `[${code}, ${generateResponseBody(response)}]`;
|
|
334
|
+
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/generator/generator.ts
|
|
337
|
+
const generate$1 = (name, doc) => `
|
|
338
|
+
/**
|
|
339
|
+
* This file was auto-generated.
|
|
340
|
+
* Do not make direct changes to the file.
|
|
341
|
+
*/
|
|
342
|
+
|
|
343
|
+
import type {
|
|
344
|
+
APIResponse,
|
|
345
|
+
APIServerDefinition,
|
|
346
|
+
BaseClient,
|
|
347
|
+
ExpressRequest,
|
|
348
|
+
GenericRouteHandler,
|
|
349
|
+
LowerCaseHeaders,
|
|
350
|
+
PartiallySerialized,
|
|
351
|
+
QueryParams,
|
|
352
|
+
RequestOptions,
|
|
353
|
+
Serialized,
|
|
354
|
+
} from '@sebspark/openapi-core'
|
|
355
|
+
|
|
356
|
+
type Req = Pick<ExpressRequest, 'url' | 'baseUrl' | 'cookies' | 'hostname'>
|
|
357
|
+
|
|
358
|
+
/* tslint:disable */
|
|
359
|
+
/* eslint-disable */
|
|
360
|
+
|
|
361
|
+
${generateComponents(doc.components)}
|
|
362
|
+
|
|
363
|
+
${doc.paths.length ? generateServer(name, doc.paths) : ""}
|
|
364
|
+
|
|
365
|
+
${doc.paths.length ? generateClient(name, doc.paths) : ""}
|
|
366
|
+
|
|
367
|
+
`;
|
|
368
|
+
const generateComponents = (components) => {
|
|
369
|
+
const tokens = [];
|
|
370
|
+
for (const schema of components.schemas) tokens.push(generateType(schema));
|
|
371
|
+
for (const header of components.headers) tokens.push(generateHeader(header));
|
|
372
|
+
for (const param$1 of components.parameters) tokens.push(generateType({
|
|
373
|
+
type: "object",
|
|
374
|
+
name: param$1.name,
|
|
375
|
+
properties: [{
|
|
376
|
+
name: param$1.parameterName,
|
|
377
|
+
type: [param$1.type],
|
|
378
|
+
optional: param$1.optional
|
|
379
|
+
}]
|
|
380
|
+
}));
|
|
381
|
+
for (const req of components.requestBodies) tokens.push(generateType(req));
|
|
382
|
+
for (const res of components.responseBodies) tokens.push(generateResponseBody(res));
|
|
383
|
+
for (const param$1 of components.securitySchemes) tokens.push(generateType({
|
|
384
|
+
type: "object",
|
|
385
|
+
name: param$1.name,
|
|
386
|
+
properties: [{
|
|
387
|
+
name: param$1.parameterName,
|
|
388
|
+
type: [param$1.type],
|
|
389
|
+
optional: param$1.optional
|
|
390
|
+
}]
|
|
391
|
+
}));
|
|
392
|
+
return tokens.join("\n\n");
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/parser/common.ts
|
|
397
|
+
const parseRef = (ref) => ref.substring(ref.lastIndexOf("/") + 1);
|
|
398
|
+
const parseEnumType = (name, schema) => ({
|
|
399
|
+
name,
|
|
400
|
+
type: "enum",
|
|
401
|
+
values: schema.enum || []
|
|
402
|
+
});
|
|
403
|
+
const findRef = (components, ref) => {
|
|
404
|
+
const [, , path, name] = ref.split("/");
|
|
405
|
+
const schemaPath = components[path];
|
|
406
|
+
if (!schemaPath || !schemaPath[name]) throw new Error(`Cannot find ref ${ref}`);
|
|
407
|
+
return schemaPath[name];
|
|
408
|
+
};
|
|
409
|
+
const parseDocumentation = (source) => {
|
|
410
|
+
const documented = {};
|
|
411
|
+
if (source.title) documented.title = source.title;
|
|
412
|
+
if (source.description) documented.description = source.description;
|
|
413
|
+
return documented;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
//#endregion
|
|
417
|
+
//#region src/parser/schema.ts
|
|
418
|
+
const parseSchemas = (schemas = {}) => Object.entries(schemas || {}).map(([name, schema]) => parseSchema(name, schema));
|
|
419
|
+
const marshall = (type, format$2) => {
|
|
420
|
+
if (type === "integer") return "number";
|
|
421
|
+
if (type === "string" && (format$2 === "date" || format$2 === "date-time")) return "Date";
|
|
422
|
+
return type;
|
|
423
|
+
};
|
|
424
|
+
const parseSchema = (name, schemaOrRef, generateDocs$1 = true) => {
|
|
425
|
+
const ref = schemaOrRef.$ref;
|
|
426
|
+
if (ref) return {
|
|
427
|
+
name,
|
|
428
|
+
type: parseRef(ref)
|
|
429
|
+
};
|
|
430
|
+
const schema = schemaOrRef;
|
|
431
|
+
switch (schema.type) {
|
|
432
|
+
case "array": return parseArraySchema(name, schema);
|
|
433
|
+
case "boolean":
|
|
434
|
+
case "integer":
|
|
435
|
+
case "number":
|
|
436
|
+
case "string": return schema.enum ? parseEnumType(name, schema) : name ? {
|
|
437
|
+
name,
|
|
438
|
+
type: marshall(schema.type, schema.format)
|
|
439
|
+
} : parsePropertyType(schema, generateDocs$1)[0];
|
|
440
|
+
default: return parseObjectSchema(name, schema);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const parseObjectSchema = (name, schema) => {
|
|
444
|
+
const type = {
|
|
445
|
+
name,
|
|
446
|
+
type: "object",
|
|
447
|
+
properties: [],
|
|
448
|
+
...parseDocumentation(schema)
|
|
449
|
+
};
|
|
450
|
+
if (schema.properties) type.properties = Object.entries(schema.properties).map(([name$1, property]) => parseProperty(name$1, property, schema.required || []));
|
|
451
|
+
if (schema.allOf) type.allOf = schema.allOf.flatMap((s) => parsePropertyType(s));
|
|
452
|
+
if (schema.oneOf) type.oneOf = schema.oneOf.flatMap((s) => parsePropertyType(s));
|
|
453
|
+
if (schema.anyOf) type.oneOf = schema.anyOf.flatMap((s) => parsePropertyType(s));
|
|
454
|
+
if (schema.discriminator?.mapping) {
|
|
455
|
+
const mapping = {};
|
|
456
|
+
for (const [prop, ref] of Object.entries(schema.discriminator.mapping)) mapping[prop] = { type: parseRef(ref) };
|
|
457
|
+
type.discriminator = {
|
|
458
|
+
propertyName: schema.discriminator.propertyName,
|
|
459
|
+
mapping
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (schema.additionalProperties) {
|
|
463
|
+
const record = parseAdditionalProperties(schema.additionalProperties);
|
|
464
|
+
if (!type.allOf) type.allOf = [];
|
|
465
|
+
type.allOf.push(record);
|
|
466
|
+
}
|
|
467
|
+
return type;
|
|
468
|
+
};
|
|
469
|
+
const parseAdditionalProperties = (schema) => {
|
|
470
|
+
let items;
|
|
471
|
+
if (schema === true) items = { type: "undefined" };
|
|
472
|
+
else items = parseSchema(void 0, schema);
|
|
473
|
+
return {
|
|
474
|
+
type: "record",
|
|
475
|
+
items
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
const parseArraySchema = (name, schema) => {
|
|
479
|
+
if (schema.type !== "array") throw new Error("Not an array");
|
|
480
|
+
return {
|
|
481
|
+
name,
|
|
482
|
+
type: "array",
|
|
483
|
+
items: schema.items ? parseSchema(void 0, schema.items, false) : { type: "unknown" },
|
|
484
|
+
...parseDocumentation(schema)
|
|
485
|
+
};
|
|
486
|
+
};
|
|
487
|
+
const parseProperty = (name, schema, required) => {
|
|
488
|
+
return {
|
|
489
|
+
name,
|
|
490
|
+
optional: !required.includes(name),
|
|
491
|
+
type: parsePropertyType(schema),
|
|
492
|
+
...parseDocumentation(schema)
|
|
493
|
+
};
|
|
494
|
+
};
|
|
495
|
+
const parsePropertyType = (property, generateDocs$1 = true) => {
|
|
496
|
+
const ref = property.$ref;
|
|
497
|
+
if (ref) return [{ type: parseRef(ref) }];
|
|
498
|
+
const schemaObject = property;
|
|
499
|
+
const docs = generateDocs$1 ? parseDocumentation(schemaObject) : {};
|
|
500
|
+
if (schemaObject.enum) return [{
|
|
501
|
+
type: "enum",
|
|
502
|
+
values: schemaObject.enum,
|
|
503
|
+
...docs
|
|
504
|
+
}];
|
|
505
|
+
if (schemaObject.type) return (Array.isArray(schemaObject.type) ? schemaObject.type : [schemaObject.type]).map((type) => {
|
|
506
|
+
switch (type) {
|
|
507
|
+
case "array": return parseArraySchema(void 0, schemaObject);
|
|
508
|
+
case "object": return parseObjectSchema(void 0, schemaObject);
|
|
509
|
+
default: return {
|
|
510
|
+
type: marshall(type, schemaObject.format),
|
|
511
|
+
...docs
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
if (schemaObject.allOf) {
|
|
516
|
+
const types = [];
|
|
517
|
+
for (const allOf of schemaObject.allOf) {
|
|
518
|
+
const type = parseSchema(void 0, allOf);
|
|
519
|
+
delete type.name;
|
|
520
|
+
types.push(type);
|
|
521
|
+
}
|
|
522
|
+
return types;
|
|
523
|
+
}
|
|
524
|
+
return [];
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region src/parser/headers.ts
|
|
529
|
+
const parseHeaders = (schemas = {}) => Object.entries(schemas || {}).map(([name, schema]) => parseHeader(name, schema));
|
|
530
|
+
const parseHeader = (name, schema) => {
|
|
531
|
+
return {
|
|
532
|
+
name,
|
|
533
|
+
optional: !schema.required,
|
|
534
|
+
type: parseSchema(void 0, schema.schema),
|
|
535
|
+
...parseDocumentation(schema)
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/parser/parameters.ts
|
|
541
|
+
const parseParameters$1 = (schemas = {}) => Object.entries(schemas || {}).map(([name, schema]) => parseParameter(name, schema));
|
|
542
|
+
const parseParameter = (name, schema) => {
|
|
543
|
+
return {
|
|
544
|
+
name,
|
|
545
|
+
in: schema.in,
|
|
546
|
+
parameterName: schema.name,
|
|
547
|
+
optional: !schema.required,
|
|
548
|
+
type: parseSchema(void 0, schema.schema),
|
|
549
|
+
...parseDocumentation(schema)
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/parser/args.ts
|
|
555
|
+
const parseArgs = (path, components) => {
|
|
556
|
+
if (!path.parameters?.length && !path.security?.length && !path.requestBody) return void 0;
|
|
557
|
+
return joinArgs([
|
|
558
|
+
parseParameters(path.parameters, components),
|
|
559
|
+
parseSecurity(path.security, components),
|
|
560
|
+
parseRequestBody(path.requestBody, components)
|
|
561
|
+
]);
|
|
562
|
+
};
|
|
563
|
+
const createArgs = (initializer = {}) => ({
|
|
564
|
+
type: "object",
|
|
565
|
+
properties: [],
|
|
566
|
+
optional: true,
|
|
567
|
+
...initializer
|
|
568
|
+
});
|
|
569
|
+
const joinArgs = (args) => {
|
|
570
|
+
const reqArg = {};
|
|
571
|
+
for (const arg of args) for (const [prop, val] of Object.entries(arg)) {
|
|
572
|
+
const key = prop;
|
|
573
|
+
if (reqArg[key]) reqArg[key] = joinArg(reqArg[key], val);
|
|
574
|
+
else reqArg[key] = val;
|
|
575
|
+
}
|
|
576
|
+
return reqArg;
|
|
577
|
+
};
|
|
578
|
+
const joinArg = (arg1, arg2) => {
|
|
579
|
+
const arg = {
|
|
580
|
+
type: "object",
|
|
581
|
+
optional: arg1.optional && arg2.optional,
|
|
582
|
+
properties: arg1.properties.concat(arg2.properties)
|
|
583
|
+
};
|
|
584
|
+
if (arg1.allOf || arg2.allOf) arg.allOf = (arg1.allOf || []).concat(arg2.allOf || []);
|
|
585
|
+
if (arg1.anyOf || arg2.anyOf) arg.anyOf = (arg1.anyOf || []).concat(arg2.anyOf || []);
|
|
586
|
+
if (arg1.oneOf || arg2.oneOf) arg.oneOf = (arg1.oneOf || []).concat(arg2.oneOf || []);
|
|
587
|
+
if (arg1.description || arg2.description) arg.description = arg1.description || arg2.description;
|
|
588
|
+
if (arg1.title || arg2.title) arg.title = arg1.title || arg2.title;
|
|
589
|
+
return arg;
|
|
590
|
+
};
|
|
591
|
+
const parseSecurity = (security = [], components = {}) => {
|
|
592
|
+
const args = {};
|
|
593
|
+
for (const secReq of security) for (const [name] of Object.entries(secReq)) {
|
|
594
|
+
const param$1 = findRef(components, `#/components/securitySchemes/${name}`);
|
|
595
|
+
const arg = args.header || createArgs({ ...parseDocumentation(param$1) });
|
|
596
|
+
arg.optional = false;
|
|
597
|
+
if (!arg.allOf) arg.allOf = [];
|
|
598
|
+
arg.allOf.push({ type: parseRef(name) });
|
|
599
|
+
args.header = arg;
|
|
600
|
+
}
|
|
601
|
+
return args;
|
|
602
|
+
};
|
|
603
|
+
const parseParameters = (parameters = [], components = {}) => {
|
|
604
|
+
const args = {};
|
|
605
|
+
for (const p of parameters) {
|
|
606
|
+
const ref = p.$ref;
|
|
607
|
+
if (ref) switch (ref.split("/")[2]) {
|
|
608
|
+
case "parameters": {
|
|
609
|
+
const param$1 = findRef(components, ref);
|
|
610
|
+
const arg = args[param$1.in] || createArgs({ ...parseDocumentation(param$1) });
|
|
611
|
+
arg.optional = arg.optional && !param$1.required;
|
|
612
|
+
if (!arg.allOf) arg.allOf = [];
|
|
613
|
+
arg.allOf.push({ type: parseRef(ref) });
|
|
614
|
+
args[param$1.in] = arg;
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
case "headers": {
|
|
618
|
+
const header = findRef(components, ref);
|
|
619
|
+
const arg = args.header || createArgs();
|
|
620
|
+
const name = parseRef(ref);
|
|
621
|
+
arg.properties.push({
|
|
622
|
+
name,
|
|
623
|
+
optional: !header.required,
|
|
624
|
+
type: [{ type: parseSchema(void 0, header.schema).type }],
|
|
625
|
+
...parseDocumentation(header.schema || {})
|
|
626
|
+
});
|
|
627
|
+
args.header = arg;
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
const param$1 = p;
|
|
633
|
+
const arg = args[param$1.in] || createArgs({ ...parseDocumentation(param$1) });
|
|
634
|
+
arg.properties.push({
|
|
635
|
+
name: param$1.name,
|
|
636
|
+
optional: !param$1.required,
|
|
637
|
+
type: [parseSchema(void 0, param$1.schema)]
|
|
638
|
+
});
|
|
639
|
+
arg.optional = arg.optional && !param$1.required;
|
|
640
|
+
args[param$1.in] = arg;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return args;
|
|
644
|
+
};
|
|
645
|
+
const parseRequestBody = (requestBody, components = {}) => {
|
|
646
|
+
const args = {};
|
|
647
|
+
if (!requestBody) return args;
|
|
648
|
+
const ref = requestBody.$ref;
|
|
649
|
+
if (ref) args.body = createArgs({
|
|
650
|
+
optional: !findRef(components, ref).required,
|
|
651
|
+
allOf: [{ type: parseRef(ref) }]
|
|
652
|
+
});
|
|
653
|
+
else {
|
|
654
|
+
const body = requestBody;
|
|
655
|
+
const bodyArgs = args.body || createArgs({
|
|
656
|
+
optional: !body.required,
|
|
657
|
+
...parseDocumentation(body)
|
|
658
|
+
});
|
|
659
|
+
if (body.content["application/json"]) {
|
|
660
|
+
const schema = body.content["application/json"].schema;
|
|
661
|
+
if (schema) {
|
|
662
|
+
const parsed = parseSchema(void 0, schema);
|
|
663
|
+
if (parsed.type === "object") args.body = {
|
|
664
|
+
...parsed,
|
|
665
|
+
optional: !body.required
|
|
666
|
+
};
|
|
667
|
+
else if (parsed.type) args.body = createArgs({
|
|
668
|
+
optional: !body.required,
|
|
669
|
+
allOf: [parsed],
|
|
670
|
+
...parseDocumentation(body)
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (bodyArgs.allOf?.length || bodyArgs.oneOf?.length || bodyArgs.properties.length) args.body = bodyArgs;
|
|
675
|
+
}
|
|
676
|
+
return args;
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/parser/responseBodies.ts
|
|
681
|
+
const parseResponseBodies = (responses = {}) => {
|
|
682
|
+
const bodies = [];
|
|
683
|
+
for (const [name, b] of Object.entries(responses)) {
|
|
684
|
+
const body = parseResponseBody(name, b);
|
|
685
|
+
bodies.push(body);
|
|
686
|
+
}
|
|
687
|
+
return bodies;
|
|
688
|
+
};
|
|
689
|
+
const parseResponseBody = (name, response) => {
|
|
690
|
+
const ref = response.$ref;
|
|
691
|
+
if (ref) return { type: parseRef(ref) };
|
|
692
|
+
const responseObject = response;
|
|
693
|
+
const body = {};
|
|
694
|
+
if (name) body.name = name;
|
|
695
|
+
if (responseObject.description) body.description = responseObject.description;
|
|
696
|
+
if (responseObject.content?.["application/json"]?.schema) {
|
|
697
|
+
const schema = responseObject.content["application/json"].schema;
|
|
698
|
+
body.data = parseSchema(void 0, schema);
|
|
699
|
+
}
|
|
700
|
+
if (responseObject.headers) {
|
|
701
|
+
body.headers = [];
|
|
702
|
+
for (const [headerName, header] of Object.entries(responseObject.headers)) {
|
|
703
|
+
const ref$1 = header.$ref;
|
|
704
|
+
if (ref$1) body.headers.push({
|
|
705
|
+
name: headerName,
|
|
706
|
+
optional: false,
|
|
707
|
+
type: { type: parseRef(ref$1) },
|
|
708
|
+
...parseDocumentation(header)
|
|
709
|
+
});
|
|
710
|
+
else body.headers.push(parseHeader(headerName, header));
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return body;
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
//#endregion
|
|
717
|
+
//#region src/parser/paths.ts
|
|
718
|
+
const parsePaths = (doc) => Object.entries(doc.paths || {}).flatMap(([name, path]) => parsePath(name, path, doc.components));
|
|
719
|
+
const parsePath = (url, path, components) => {
|
|
720
|
+
const paths = [];
|
|
721
|
+
for (const method of [
|
|
722
|
+
"delete",
|
|
723
|
+
"get",
|
|
724
|
+
"patch",
|
|
725
|
+
"post",
|
|
726
|
+
"put"
|
|
727
|
+
]) if (path[method]) paths.push(parseMethod(url, method, path[method], components));
|
|
728
|
+
return paths;
|
|
729
|
+
};
|
|
730
|
+
const parseMethod = (url, method, operation, components) => {
|
|
731
|
+
return {
|
|
732
|
+
method,
|
|
733
|
+
url: parseUrl(url),
|
|
734
|
+
responses: parseResponses(operation.responses),
|
|
735
|
+
args: parseArgs(operation, components),
|
|
736
|
+
...parseDocumentation(operation)
|
|
737
|
+
};
|
|
738
|
+
};
|
|
739
|
+
const parseUrl = (url) => url.replace(/{([^}]+)}/g, ":$1");
|
|
740
|
+
const parseResponses = (responses) => {
|
|
741
|
+
return Object.assign({}, ...Object.entries(responses).map(([code, response]) => {
|
|
742
|
+
return { [Number.parseInt(code, 10)]: parseResponseBody(void 0, response) };
|
|
743
|
+
}));
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/parser/requestBodies.ts
|
|
748
|
+
const parseRequestBodies = (requestBodies = {}) => {
|
|
749
|
+
const definitions = [];
|
|
750
|
+
for (const [name, requestBody] of Object.entries(requestBodies)) if (requestBody.content["application/json"].schema) definitions.push(parseSchema(name, requestBody.content["application/json"].schema));
|
|
751
|
+
return definitions;
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
//#endregion
|
|
755
|
+
//#region src/parser/securitySchemes.ts
|
|
756
|
+
const parseSecuritySchemes = (schemes = {}) => {
|
|
757
|
+
const parameters = [];
|
|
758
|
+
for (const [name, scheme] of Object.entries(schemes)) parameters.push(parseSecurityScheme(name, scheme));
|
|
759
|
+
return parameters;
|
|
760
|
+
};
|
|
761
|
+
const parseSecurityScheme = (name, scheme) => {
|
|
762
|
+
switch (scheme.type) {
|
|
763
|
+
case "apiKey": return parseApiKey(name, scheme);
|
|
764
|
+
case "http": return parseHttpSecurity(name, scheme);
|
|
765
|
+
case "oauth2": return parseOAuth(name, scheme);
|
|
766
|
+
case "openIdConnect": return parseOpenIdConnect(name, scheme);
|
|
767
|
+
}
|
|
768
|
+
throw new Error(`Unknown security scheme '${scheme.type}'`);
|
|
769
|
+
};
|
|
770
|
+
const parseApiKey = (name, scheme) => {
|
|
771
|
+
const _in = scheme.in || "header";
|
|
772
|
+
return {
|
|
773
|
+
name,
|
|
774
|
+
parameterName: scheme.name,
|
|
775
|
+
in: _in,
|
|
776
|
+
optional: false,
|
|
777
|
+
type: { type: "string" }
|
|
778
|
+
};
|
|
779
|
+
};
|
|
780
|
+
const parseHttpSecurity = (name, _scheme) => ({
|
|
781
|
+
name,
|
|
782
|
+
in: "header",
|
|
783
|
+
parameterName: "Authorization",
|
|
784
|
+
optional: false,
|
|
785
|
+
type: { type: "string" }
|
|
786
|
+
});
|
|
787
|
+
const parseOAuth = (name, _scheme) => ({
|
|
788
|
+
name,
|
|
789
|
+
in: "header",
|
|
790
|
+
parameterName: "Authorization",
|
|
791
|
+
optional: false,
|
|
792
|
+
type: { type: "string" }
|
|
793
|
+
});
|
|
794
|
+
const parseOpenIdConnect = (name, _scheme) => ({
|
|
795
|
+
name,
|
|
796
|
+
in: "header",
|
|
797
|
+
parameterName: "Authorization",
|
|
798
|
+
optional: false,
|
|
799
|
+
type: { type: "string" }
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
//#endregion
|
|
803
|
+
//#region src/parser/index.ts
|
|
804
|
+
const parseDocument = (schema) => ({
|
|
805
|
+
paths: parsePaths(schema),
|
|
806
|
+
components: parseComponents(schema.components)
|
|
807
|
+
});
|
|
808
|
+
const parseComponents = (components = {}) => ({
|
|
809
|
+
schemas: parseSchemas(components.schemas),
|
|
810
|
+
headers: parseHeaders(components.headers),
|
|
811
|
+
parameters: parseParameters$1(components.parameters),
|
|
812
|
+
requestBodies: parseRequestBodies(components.requestBodies),
|
|
813
|
+
responseBodies: parseResponseBodies(components.responses),
|
|
814
|
+
securitySchemes: parseSecuritySchemes(components.securitySchemes)
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
//#endregion
|
|
818
|
+
//#region src/index.ts
|
|
819
|
+
const generateTypescript = async (name, doc) => {
|
|
820
|
+
return await format$1(generate$1(name, parseDocument(doc)));
|
|
821
|
+
};
|
|
822
|
+
const generate = async (input, output) => {
|
|
823
|
+
const generated = await generateDocs(await readDocs(input));
|
|
824
|
+
if (!output) return generated.map((d) => d.ts).join("\n\n");
|
|
825
|
+
await saveDocs(output, generated);
|
|
826
|
+
};
|
|
827
|
+
const readDocs = async (input) => {
|
|
828
|
+
const path = resolve(input);
|
|
829
|
+
const stats = await stat(path);
|
|
830
|
+
const filePaths = [];
|
|
831
|
+
if (stats.isFile()) filePaths.push(path);
|
|
832
|
+
if (stats.isDirectory()) {
|
|
833
|
+
const files = await readdir(path);
|
|
834
|
+
filePaths.push(...files.map((f) => resolve(path, f)));
|
|
835
|
+
}
|
|
836
|
+
const readFiles = [];
|
|
837
|
+
for (const p of filePaths) {
|
|
838
|
+
const { name, ext } = parse(p);
|
|
839
|
+
let doc;
|
|
840
|
+
switch (ext) {
|
|
841
|
+
case ".json": {
|
|
842
|
+
console.log(`Reading ${p}`);
|
|
843
|
+
const txt = await readFile(p, "utf8");
|
|
844
|
+
doc = JSON.parse(txt);
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
case ".yml":
|
|
848
|
+
case ".yaml": {
|
|
849
|
+
console.log(`Reading ${p}`);
|
|
850
|
+
const txt = await readFile(p, "utf8");
|
|
851
|
+
doc = YAML.parse(txt);
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
default: continue;
|
|
855
|
+
}
|
|
856
|
+
readFiles.push({
|
|
857
|
+
doc,
|
|
858
|
+
name
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
return readFiles;
|
|
862
|
+
};
|
|
863
|
+
const generateDocs = async (files) => {
|
|
864
|
+
const generated = [];
|
|
865
|
+
for (const doc of files) {
|
|
866
|
+
console.log(`Generating ${doc.name}`);
|
|
867
|
+
const ts = await generateTypescript(classname(doc.name), doc.doc);
|
|
868
|
+
generated.push({
|
|
869
|
+
...doc,
|
|
870
|
+
ts
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
return generated;
|
|
874
|
+
};
|
|
875
|
+
const saveDocs = async (output, docs) => {
|
|
876
|
+
const dir = (await stat(output)).isDirectory() ? output : parse(output).dir;
|
|
877
|
+
await mkdir(dir, { recursive: true });
|
|
878
|
+
for (const doc of docs) {
|
|
879
|
+
const path = resolve(dir, `${filename(doc.name)}.ts`);
|
|
880
|
+
console.log(`Writing ${path}`);
|
|
881
|
+
await writeFile(path, doc.ts, "utf8");
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
const classname = (name) => {
|
|
885
|
+
return pascalCase(name.replace(/\d+/g, ""));
|
|
886
|
+
};
|
|
887
|
+
const filename = (name) => {
|
|
888
|
+
return name.replace(/\./g, "_");
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
//#endregion
|
|
892
|
+
export { classname, filename, generate, generateTypescript };
|
|
893
|
+
//# sourceMappingURL=index.mjs.map
|