@f3liz/rescript-autogen-openapi 0.3.1
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/LICENSE +373 -0
- package/README.md +111 -0
- package/lib/es6/src/Codegen.d.ts +28 -0
- package/lib/es6/src/Codegen.mjs +423 -0
- package/lib/es6/src/Types.d.ts +286 -0
- package/lib/es6/src/Types.mjs +20 -0
- package/lib/es6/src/bindings/Toposort.mjs +12 -0
- package/lib/es6/src/core/CodegenUtils.mjs +261 -0
- package/lib/es6/src/core/DocOverride.mjs +399 -0
- package/lib/es6/src/core/FileSystem.d.ts +4 -0
- package/lib/es6/src/core/FileSystem.mjs +78 -0
- package/lib/es6/src/core/IRBuilder.mjs +201 -0
- package/lib/es6/src/core/OpenAPIParser.mjs +168 -0
- package/lib/es6/src/core/Pipeline.d.ts +6 -0
- package/lib/es6/src/core/Pipeline.mjs +150 -0
- package/lib/es6/src/core/ReferenceResolver.mjs +41 -0
- package/lib/es6/src/core/Result.mjs +378 -0
- package/lib/es6/src/core/SchemaIR.mjs +425 -0
- package/lib/es6/src/core/SchemaIRParser.mjs +683 -0
- package/lib/es6/src/core/SchemaRefResolver.mjs +146 -0
- package/lib/es6/src/core/SchemaRegistry.mjs +92 -0
- package/lib/es6/src/core/SpecDiffer.mjs +251 -0
- package/lib/es6/src/core/SpecMerger.mjs +237 -0
- package/lib/es6/src/generators/ComponentSchemaGenerator.mjs +207 -0
- package/lib/es6/src/generators/DiffReportGenerator.mjs +155 -0
- package/lib/es6/src/generators/EndpointGenerator.mjs +173 -0
- package/lib/es6/src/generators/IRToSuryGenerator.mjs +543 -0
- package/lib/es6/src/generators/IRToTypeGenerator.mjs +592 -0
- package/lib/es6/src/generators/IRToTypeScriptGenerator.mjs +143 -0
- package/lib/es6/src/generators/ModuleGenerator.mjs +285 -0
- package/lib/es6/src/generators/SchemaCodeGenerator.mjs +77 -0
- package/lib/es6/src/generators/ThinWrapperGenerator.mjs +97 -0
- package/lib/es6/src/generators/TypeScriptDtsGenerator.mjs +172 -0
- package/lib/es6/src/generators/TypeScriptWrapperGenerator.mjs +145 -0
- package/lib/es6/src/types/CodegenError.d.ts +66 -0
- package/lib/es6/src/types/CodegenError.mjs +79 -0
- package/lib/es6/src/types/Config.d.ts +31 -0
- package/lib/es6/src/types/Config.mjs +42 -0
- package/lib/es6/src/types/GenerationContext.mjs +47 -0
- package/package.json +53 -0
- package/rescript.json +26 -0
- package/src/Codegen.res +231 -0
- package/src/Types.res +222 -0
- package/src/bindings/Toposort.res +16 -0
- package/src/core/CodegenUtils.res +180 -0
- package/src/core/DocOverride.res +504 -0
- package/src/core/FileSystem.res +63 -0
- package/src/core/IRBuilder.res +66 -0
- package/src/core/OpenAPIParser.res +144 -0
- package/src/core/Pipeline.res +52 -0
- package/src/core/ReferenceResolver.res +41 -0
- package/src/core/Result.res +187 -0
- package/src/core/SchemaIR.res +291 -0
- package/src/core/SchemaIRParser.res +454 -0
- package/src/core/SchemaRefResolver.res +143 -0
- package/src/core/SchemaRegistry.res +107 -0
- package/src/core/SpecDiffer.res +270 -0
- package/src/core/SpecMerger.res +245 -0
- package/src/generators/ComponentSchemaGenerator.res +210 -0
- package/src/generators/DiffReportGenerator.res +152 -0
- package/src/generators/EndpointGenerator.res +176 -0
- package/src/generators/IRToSuryGenerator.res +386 -0
- package/src/generators/IRToTypeGenerator.res +423 -0
- package/src/generators/IRToTypeScriptGenerator.res +77 -0
- package/src/generators/ModuleGenerator.res +363 -0
- package/src/generators/SchemaCodeGenerator.res +84 -0
- package/src/generators/ThinWrapperGenerator.res +124 -0
- package/src/generators/TypeScriptDtsGenerator.res +193 -0
- package/src/generators/TypeScriptWrapperGenerator.res +166 -0
- package/src/types/CodegenError.res +85 -0
- package/src/types/Config.res +95 -0
- package/src/types/GenerationContext.res +56 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as CodegenError from "./types/CodegenError.mjs";
|
|
4
|
+
|
|
5
|
+
let Warning_toString = CodegenError.Warning.toString;
|
|
6
|
+
|
|
7
|
+
let Warning_print = CodegenError.Warning.print;
|
|
8
|
+
|
|
9
|
+
let Warning = {
|
|
10
|
+
toString: Warning_toString,
|
|
11
|
+
print: Warning_print
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let CodegenError$1;
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
CodegenError$1 as CodegenError,
|
|
18
|
+
Warning,
|
|
19
|
+
}
|
|
20
|
+
/* No side effect */
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as JsConvertCase from "js-convert-case";
|
|
5
|
+
|
|
6
|
+
function sanitizeIdentifier(str) {
|
|
7
|
+
return str.replaceAll("{", "").replaceAll("}", "").replaceAll("[", "").replaceAll("]", "").replaceAll(".", "_").replaceAll("-", "_").replaceAll("/", "_").replaceAll(" ", "_");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function generateTypeName(prefixOpt, path, suffix) {
|
|
11
|
+
let prefix = prefixOpt !== undefined ? prefixOpt : "";
|
|
12
|
+
let cleaned = sanitizeIdentifier(path.replaceAll("/", "_")).split("_").filter(part => part !== "").map(prim => JsConvertCase.toPascalCase(prim)).join("");
|
|
13
|
+
return prefix + cleaned + suffix;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function generateOperationName(operationId, path, method) {
|
|
17
|
+
if (operationId !== undefined) {
|
|
18
|
+
return JsConvertCase.toCamelCase(sanitizeIdentifier(operationId));
|
|
19
|
+
} else {
|
|
20
|
+
return method.toLowerCase() + path.split("/").filter(part => {
|
|
21
|
+
if (part !== "") {
|
|
22
|
+
return !part.startsWith("{");
|
|
23
|
+
} else {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}).map(prim => JsConvertCase.toCamelCase(prim)).join("");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function escapeString(str) {
|
|
31
|
+
return str.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll("\t", "\\t");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function generateFileHeader(description) {
|
|
35
|
+
return `// ` + description + `
|
|
36
|
+
// Generated by @f3liz/rescript-autogen-openapi
|
|
37
|
+
// DO NOT EDIT - This file is auto-generated
|
|
38
|
+
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function indent(code, level) {
|
|
43
|
+
let spaces = " ".repeat(level);
|
|
44
|
+
return code.split("\n").map(line => {
|
|
45
|
+
if (line.trim() === "") {
|
|
46
|
+
return "";
|
|
47
|
+
} else {
|
|
48
|
+
return spaces + line;
|
|
49
|
+
}
|
|
50
|
+
}).join("\n");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function trimMargin(text, marginPrefixOpt) {
|
|
54
|
+
let marginPrefix = marginPrefixOpt !== undefined ? marginPrefixOpt : "|";
|
|
55
|
+
return text.split("\n").map(line => {
|
|
56
|
+
let trimmed = line.trimStart();
|
|
57
|
+
if (trimmed.startsWith(marginPrefix)) {
|
|
58
|
+
return trimmed.slice(marginPrefix.length);
|
|
59
|
+
} else {
|
|
60
|
+
return line;
|
|
61
|
+
}
|
|
62
|
+
}).join("\n").trim();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let rescriptKeywords = [
|
|
66
|
+
"and",
|
|
67
|
+
"as",
|
|
68
|
+
"assert",
|
|
69
|
+
"async",
|
|
70
|
+
"await",
|
|
71
|
+
"catch",
|
|
72
|
+
"class",
|
|
73
|
+
"constraint",
|
|
74
|
+
"do",
|
|
75
|
+
"done",
|
|
76
|
+
"downto",
|
|
77
|
+
"else",
|
|
78
|
+
"end",
|
|
79
|
+
"exception",
|
|
80
|
+
"external",
|
|
81
|
+
"false",
|
|
82
|
+
"for",
|
|
83
|
+
"fun",
|
|
84
|
+
"function",
|
|
85
|
+
"functor",
|
|
86
|
+
"if",
|
|
87
|
+
"in",
|
|
88
|
+
"include",
|
|
89
|
+
"inherit",
|
|
90
|
+
"initializer",
|
|
91
|
+
"lazy",
|
|
92
|
+
"let",
|
|
93
|
+
"method",
|
|
94
|
+
"module",
|
|
95
|
+
"mutable",
|
|
96
|
+
"new",
|
|
97
|
+
"nonrec",
|
|
98
|
+
"object",
|
|
99
|
+
"of",
|
|
100
|
+
"open",
|
|
101
|
+
"or",
|
|
102
|
+
"private",
|
|
103
|
+
"rec",
|
|
104
|
+
"sig",
|
|
105
|
+
"struct",
|
|
106
|
+
"switch",
|
|
107
|
+
"then",
|
|
108
|
+
"to",
|
|
109
|
+
"true",
|
|
110
|
+
"try",
|
|
111
|
+
"type",
|
|
112
|
+
"val",
|
|
113
|
+
"virtual",
|
|
114
|
+
"when",
|
|
115
|
+
"while",
|
|
116
|
+
"with"
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
function escapeKeyword(name) {
|
|
120
|
+
if (rescriptKeywords.includes(name)) {
|
|
121
|
+
return name + "_";
|
|
122
|
+
} else {
|
|
123
|
+
return name;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function generateDocComment(summary, description, param) {
|
|
128
|
+
if (summary !== undefined) {
|
|
129
|
+
if (description !== undefined) {
|
|
130
|
+
return `// ` + summary + `\n// ` + description + `\n`;
|
|
131
|
+
} else {
|
|
132
|
+
return `// ` + summary + `\n`;
|
|
133
|
+
}
|
|
134
|
+
} else if (description !== undefined) {
|
|
135
|
+
return `// ` + description + `\n`;
|
|
136
|
+
} else {
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function generateDocString(summary, description, param) {
|
|
142
|
+
let content = summary !== undefined ? (
|
|
143
|
+
description !== undefined ? (
|
|
144
|
+
summary === description ? summary : summary + "\n\n" + description
|
|
145
|
+
) : summary
|
|
146
|
+
) : (
|
|
147
|
+
description !== undefined ? description : undefined
|
|
148
|
+
);
|
|
149
|
+
return Stdlib_Option.getOr(Stdlib_Option.map(content, text => {
|
|
150
|
+
let lines = text.trim().split("\n").map(prim => prim.trim());
|
|
151
|
+
let len = lines.length;
|
|
152
|
+
if (len !== 1) {
|
|
153
|
+
if (len !== 0) {
|
|
154
|
+
return "/**\n" + lines.map(l => {
|
|
155
|
+
if (l === "") {
|
|
156
|
+
return " *";
|
|
157
|
+
} else {
|
|
158
|
+
return ` * ` + l;
|
|
159
|
+
}
|
|
160
|
+
}).join("\n") + "\n */\n";
|
|
161
|
+
} else {
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
let line = lines[0];
|
|
166
|
+
return `/** ` + line + ` */\n`;
|
|
167
|
+
}), "");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function variantConstructorName(_irType) {
|
|
171
|
+
while (true) {
|
|
172
|
+
let irType = _irType;
|
|
173
|
+
if (typeof irType !== "object") {
|
|
174
|
+
switch (irType) {
|
|
175
|
+
case "Boolean" :
|
|
176
|
+
return "Bool";
|
|
177
|
+
case "Null" :
|
|
178
|
+
return "Null";
|
|
179
|
+
case "Unknown" :
|
|
180
|
+
return "Unknown";
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
switch (irType.TAG) {
|
|
184
|
+
case "String" :
|
|
185
|
+
return "String";
|
|
186
|
+
case "Number" :
|
|
187
|
+
return "Float";
|
|
188
|
+
case "Integer" :
|
|
189
|
+
return "Int";
|
|
190
|
+
case "Array" :
|
|
191
|
+
return "Array";
|
|
192
|
+
case "Object" :
|
|
193
|
+
return "Object";
|
|
194
|
+
case "Literal" :
|
|
195
|
+
let s = irType._0;
|
|
196
|
+
if (typeof s !== "object") {
|
|
197
|
+
return "Null";
|
|
198
|
+
}
|
|
199
|
+
switch (s.TAG) {
|
|
200
|
+
case "StringLiteral" :
|
|
201
|
+
return JsConvertCase.toPascalCase(s._0);
|
|
202
|
+
case "NumberLiteral" :
|
|
203
|
+
return "Number";
|
|
204
|
+
case "BooleanLiteral" :
|
|
205
|
+
return "Bool";
|
|
206
|
+
}
|
|
207
|
+
case "Union" :
|
|
208
|
+
return "Union";
|
|
209
|
+
case "Intersection" :
|
|
210
|
+
return "Intersection";
|
|
211
|
+
case "Reference" :
|
|
212
|
+
let ref = irType._0;
|
|
213
|
+
return JsConvertCase.toPascalCase(ref.includes("/") ? Stdlib_Option.getOr(ref.split("/")[ref.split("/").length - 1 | 0], "Ref") : ref);
|
|
214
|
+
case "Option" :
|
|
215
|
+
_irType = irType._0;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function deduplicateNames(names) {
|
|
223
|
+
let counts = {};
|
|
224
|
+
let result = [];
|
|
225
|
+
names.forEach(name => {
|
|
226
|
+
let count = Stdlib_Option.getOr(counts[name], 0);
|
|
227
|
+
counts[name] = count + 1 | 0;
|
|
228
|
+
});
|
|
229
|
+
let seen = {};
|
|
230
|
+
names.forEach(name => {
|
|
231
|
+
let total = Stdlib_Option.getOr(counts[name], 1);
|
|
232
|
+
if (total > 1) {
|
|
233
|
+
let idx = Stdlib_Option.getOr(seen[name], 0) + 1 | 0;
|
|
234
|
+
seen[name] = idx;
|
|
235
|
+
result.push(name + idx.toString());
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
result.push(name);
|
|
239
|
+
});
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let fetchTypeSignature = "(~url: string, ~method_: string, ~body: option<JSON.t>) => Promise.t<JSON.t>";
|
|
244
|
+
|
|
245
|
+
export {
|
|
246
|
+
sanitizeIdentifier,
|
|
247
|
+
generateTypeName,
|
|
248
|
+
generateOperationName,
|
|
249
|
+
escapeString,
|
|
250
|
+
generateFileHeader,
|
|
251
|
+
indent,
|
|
252
|
+
trimMargin,
|
|
253
|
+
rescriptKeywords,
|
|
254
|
+
escapeKeyword,
|
|
255
|
+
generateDocComment,
|
|
256
|
+
generateDocString,
|
|
257
|
+
fetchTypeSignature,
|
|
258
|
+
variantConstructorName,
|
|
259
|
+
deduplicateNames,
|
|
260
|
+
}
|
|
261
|
+
/* js-convert-case Not a pure module */
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Fs from "fs";
|
|
4
|
+
import * as Js_string from "@rescript/runtime/lib/es6/Js_string.js";
|
|
5
|
+
import * as FileSystem from "./FileSystem.mjs";
|
|
6
|
+
import * as CodegenUtils from "./CodegenUtils.mjs";
|
|
7
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
9
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
|
+
import * as JsConvertCase from "js-convert-case";
|
|
11
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
12
|
+
|
|
13
|
+
function generateEndpointHash(endpoint) {
|
|
14
|
+
let parts = [
|
|
15
|
+
endpoint.path,
|
|
16
|
+
endpoint.method,
|
|
17
|
+
Stdlib_Option.getOr(endpoint.operationId, ""),
|
|
18
|
+
Stdlib_Option.getOr(endpoint.summary, ""),
|
|
19
|
+
Stdlib_Option.getOr(endpoint.description, "")
|
|
20
|
+
];
|
|
21
|
+
let combined = parts.join("|");
|
|
22
|
+
return Stdlib_Array.reduce(combined.split(""), 0, (acc, char) => {
|
|
23
|
+
let code = Js_string.charCodeAt(0, char) | 0;
|
|
24
|
+
return (((acc << 5) - acc | 0) + code | 0) % 2147483647;
|
|
25
|
+
}).toString(16);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractCodeBlock(markdown) {
|
|
29
|
+
let parts = markdown.split("```");
|
|
30
|
+
if (parts.length < 3) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let content = parts[1];
|
|
34
|
+
if (content === undefined) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let trimmed = content.trim();
|
|
38
|
+
if (trimmed === "" || trimmed === "<!-- Empty - no override -->") {
|
|
39
|
+
return;
|
|
40
|
+
} else {
|
|
41
|
+
return trimmed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseOverrideMarkdown(content) {
|
|
46
|
+
let parts = content.split("---");
|
|
47
|
+
if (parts.length < 3) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
let frontmatter = Stdlib_Option.getOr(parts[1], "");
|
|
51
|
+
let body = parts.slice(2, parts.length).join("---").trim();
|
|
52
|
+
let lines = frontmatter.split("\n").map(prim => prim.trim());
|
|
53
|
+
let metadata_endpoint = Stdlib_Option.getOr(Stdlib_Option.map(lines.find(l => l.startsWith("endpoint:")), l => {
|
|
54
|
+
let parts = l.split(":");
|
|
55
|
+
return parts.slice(1, parts.length).join(":").trim();
|
|
56
|
+
}), "");
|
|
57
|
+
let metadata_method = Stdlib_Option.getOr(Stdlib_Option.map(lines.find(l => l.startsWith("method:")), l => Stdlib_Option.getOr(l.split(":")[1], "").trim()), "");
|
|
58
|
+
let metadata_hash = Stdlib_Option.getOr(Stdlib_Option.map(lines.find(l => l.startsWith("hash:")), l => Stdlib_Option.getOr(l.split(":")[1], "").trim()), "");
|
|
59
|
+
let metadata_host = Stdlib_Option.flatMap(lines.find(l => l.startsWith("host:")), l => {
|
|
60
|
+
let parts = l.split(":");
|
|
61
|
+
return parts.slice(1, parts.length).join(":").trim();
|
|
62
|
+
});
|
|
63
|
+
let metadata_version = Stdlib_Option.flatMap(lines.find(l => l.startsWith("version:")), l => Stdlib_Option.map(l.split(":")[1], prim => prim.trim()));
|
|
64
|
+
let metadata_operationId = Stdlib_Option.flatMap(lines.find(l => l.startsWith("operationId:")), l => Stdlib_Option.map(l.split(":")[1], prim => prim.trim()));
|
|
65
|
+
let metadata = {
|
|
66
|
+
endpoint: metadata_endpoint,
|
|
67
|
+
method: metadata_method,
|
|
68
|
+
hash: metadata_hash,
|
|
69
|
+
host: metadata_host,
|
|
70
|
+
version: metadata_version,
|
|
71
|
+
operationId: metadata_operationId
|
|
72
|
+
};
|
|
73
|
+
let parts$1 = Stdlib_Option.getOr(body.split("## Override")[0], "").split("## Default Description");
|
|
74
|
+
let defaultDescSection = parts$1.slice(1, parts$1.length).join("## Default Description").trim();
|
|
75
|
+
let parts$2 = body.split("## Override");
|
|
76
|
+
let overrideSection = parts$2.slice(1, parts$2.length).join("## Override");
|
|
77
|
+
let overrideDesc = extractCodeBlock(overrideSection);
|
|
78
|
+
let hasOverride = Stdlib_Option.isSome(overrideDesc);
|
|
79
|
+
return {
|
|
80
|
+
metadata: metadata,
|
|
81
|
+
defaultDescription: defaultDescSection,
|
|
82
|
+
overrideDescription: overrideDesc,
|
|
83
|
+
hasOverride: hasOverride
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function generateOverrideMarkdown(endpoint, host, version, param) {
|
|
88
|
+
let hash = generateEndpointHash(endpoint);
|
|
89
|
+
let operationName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
|
|
90
|
+
let match = endpoint.summary;
|
|
91
|
+
let match$1 = endpoint.description;
|
|
92
|
+
let defaultDesc = match !== undefined ? (
|
|
93
|
+
match$1 !== undefined && match !== match$1 ? match + "\n\n" + match$1 : match
|
|
94
|
+
) : (
|
|
95
|
+
match$1 !== undefined ? match$1 : "No description provided."
|
|
96
|
+
);
|
|
97
|
+
let metadata = [
|
|
98
|
+
"---",
|
|
99
|
+
`endpoint: ` + endpoint.path,
|
|
100
|
+
`method: ` + endpoint.method.toUpperCase(),
|
|
101
|
+
`hash: ` + hash
|
|
102
|
+
].concat(Stdlib_Array.filterMap([
|
|
103
|
+
Stdlib_Option.map(host, h => `host: ` + h),
|
|
104
|
+
Stdlib_Option.map(version, v => `version: ` + v),
|
|
105
|
+
Stdlib_Option.map(endpoint.operationId, id => `operationId: ` + id),
|
|
106
|
+
"---"
|
|
107
|
+
], x => x));
|
|
108
|
+
let content = `
|
|
109
|
+
|` + metadata.join("\n") + `
|
|
110
|
+
|
|
|
111
|
+
|# ` + Stdlib_Option.getOr(endpoint.summary, endpoint.path) + `
|
|
112
|
+
|
|
|
113
|
+
|**Path**: \`` + endpoint.path + `\`
|
|
114
|
+
|**Method**: \`` + endpoint.method.toUpperCase() + `\`
|
|
115
|
+
|**Operation**: \`` + operationName + `\`
|
|
116
|
+
|
|
|
117
|
+
|## Default Description
|
|
118
|
+
|
|
|
119
|
+
|` + defaultDesc + `
|
|
120
|
+
|
|
|
121
|
+
|## Override
|
|
122
|
+
|
|
|
123
|
+
|Add your custom documentation here. If this code block is empty, the default description will be used.
|
|
124
|
+
|
|
|
125
|
+
|\`\`\`
|
|
126
|
+
|<!-- Empty - no override -->
|
|
127
|
+
|\`\`\`
|
|
128
|
+
|`;
|
|
129
|
+
return CodegenUtils.trimMargin(content, undefined);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function validateOverride(override, currentHash) {
|
|
133
|
+
if (override.metadata.hash === currentHash) {
|
|
134
|
+
return "Valid";
|
|
135
|
+
} else {
|
|
136
|
+
return {
|
|
137
|
+
TAG: "HashMismatch",
|
|
138
|
+
expected: currentHash,
|
|
139
|
+
found: override.metadata.hash
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function readOverrideWithValidation(overrideDir, moduleName, functionName, currentHash) {
|
|
145
|
+
let filePath = FileSystem.makePath(FileSystem.makePath(overrideDir, moduleName), functionName + ".md");
|
|
146
|
+
if (!Fs.existsSync(filePath)) {
|
|
147
|
+
return "NoOverride";
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
let content = Fs.readFileSync(filePath, {
|
|
151
|
+
encoding: "utf8"
|
|
152
|
+
});
|
|
153
|
+
let parsed = parseOverrideMarkdown(content);
|
|
154
|
+
if (parsed === undefined) {
|
|
155
|
+
return {
|
|
156
|
+
TAG: "FileError",
|
|
157
|
+
_0: "Failed to parse markdown file"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (!parsed.hasOverride) {
|
|
161
|
+
return "NoOverride";
|
|
162
|
+
}
|
|
163
|
+
let msg = validateOverride(parsed, currentHash);
|
|
164
|
+
if (typeof msg !== "object") {
|
|
165
|
+
if (msg !== "Valid") {
|
|
166
|
+
return {
|
|
167
|
+
TAG: "FileError",
|
|
168
|
+
_0: "Override file missing"
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
let desc = parsed.overrideDescription;
|
|
172
|
+
if (desc !== undefined) {
|
|
173
|
+
return {
|
|
174
|
+
TAG: "ValidOverride",
|
|
175
|
+
_0: desc
|
|
176
|
+
};
|
|
177
|
+
} else {
|
|
178
|
+
return "NoOverride";
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
if (msg.TAG !== "HashMismatch") {
|
|
182
|
+
return {
|
|
183
|
+
TAG: "FileError",
|
|
184
|
+
_0: msg._0
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
let desc$1 = parsed.overrideDescription;
|
|
188
|
+
if (desc$1 !== undefined) {
|
|
189
|
+
return {
|
|
190
|
+
TAG: "InvalidHash",
|
|
191
|
+
override: desc$1,
|
|
192
|
+
expected: msg.expected,
|
|
193
|
+
found: msg.found
|
|
194
|
+
};
|
|
195
|
+
} else {
|
|
196
|
+
return "NoOverride";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (raw_err) {
|
|
200
|
+
let err = Primitive_exceptions.internalToException(raw_err);
|
|
201
|
+
if (err.RE_EXN_ID === "JsExn") {
|
|
202
|
+
return {
|
|
203
|
+
TAG: "FileError",
|
|
204
|
+
_0: Stdlib_Option.getOr(Stdlib_JsExn.message(err._1), "Unknown error")
|
|
205
|
+
};
|
|
206
|
+
} else {
|
|
207
|
+
return {
|
|
208
|
+
TAG: "FileError",
|
|
209
|
+
_0: "Unknown error reading file"
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function readOverride(overrideDir, moduleName, functionName) {
|
|
216
|
+
let filePath = FileSystem.makePath(FileSystem.makePath(overrideDir, moduleName), functionName + ".md");
|
|
217
|
+
if (!Fs.existsSync(filePath)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
let content = Fs.readFileSync(filePath, {
|
|
222
|
+
encoding: "utf8"
|
|
223
|
+
});
|
|
224
|
+
let parsed = parseOverrideMarkdown(content);
|
|
225
|
+
if (parsed !== undefined) {
|
|
226
|
+
return parsed.overrideDescription;
|
|
227
|
+
} else {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
} catch (exn) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isFileCustomized(filePath) {
|
|
236
|
+
if (!Fs.existsSync(filePath)) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
let content = Fs.readFileSync(filePath, {
|
|
241
|
+
encoding: "utf8"
|
|
242
|
+
});
|
|
243
|
+
let parsed = parseOverrideMarkdown(content);
|
|
244
|
+
if (parsed !== undefined) {
|
|
245
|
+
return parsed.hasOverride;
|
|
246
|
+
} else {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
} catch (exn) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function generateOverrideFiles(spec, endpoints, outputDir, host, groupByTagOpt, param) {
|
|
255
|
+
let groupByTag = groupByTagOpt !== undefined ? groupByTagOpt : true;
|
|
256
|
+
let version = spec.info.version;
|
|
257
|
+
let hostUrl = host !== undefined ? host : spec.info.description;
|
|
258
|
+
return Stdlib_Array.keepSome(endpoints.map(endpoint => {
|
|
259
|
+
let moduleName;
|
|
260
|
+
if (groupByTag) {
|
|
261
|
+
let tags = endpoint.tags;
|
|
262
|
+
moduleName = tags !== undefined ? Stdlib_Option.getOr(tags[0], "Default") : "Default";
|
|
263
|
+
} else {
|
|
264
|
+
moduleName = "API";
|
|
265
|
+
}
|
|
266
|
+
let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
|
|
267
|
+
let modulePath = FileSystem.makePath(outputDir, JsConvertCase.toPascalCase(moduleName));
|
|
268
|
+
let filePath = FileSystem.makePath(modulePath, functionName + ".md");
|
|
269
|
+
if (isFileCustomized(filePath)) {
|
|
270
|
+
console.log(`ℹ️ Skipping ` + moduleName + `/` + functionName + ` - file already customized`);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
let content = generateOverrideMarkdown(endpoint, hostUrl, version, undefined);
|
|
274
|
+
return {
|
|
275
|
+
path: filePath,
|
|
276
|
+
content: content
|
|
277
|
+
};
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function generateOverrideReadme(host, version, param) {
|
|
282
|
+
let hostInfo = Stdlib_Option.getOr(host, "Not specified");
|
|
283
|
+
let versionInfo = Stdlib_Option.getOr(version, "Not specified");
|
|
284
|
+
return CodegenUtils.trimMargin(`
|
|
285
|
+
|# API Documentation Overrides
|
|
286
|
+
|
|
|
287
|
+
|This directory contains markdown files that allow you to override the auto-generated documentation.
|
|
288
|
+
|
|
|
289
|
+
|## Global Information
|
|
290
|
+
|
|
|
291
|
+
|- **Host**: ` + hostInfo + `
|
|
292
|
+
|- **Version**: ` + versionInfo + `
|
|
293
|
+
|
|
|
294
|
+
|## Structure
|
|
295
|
+
|
|
|
296
|
+
|Each module has its own directory, and each endpoint has its own markdown file:
|
|
297
|
+
|
|
|
298
|
+
|\`\`\`
|
|
299
|
+
|docs/
|
|
300
|
+
|├── README.md (this file)
|
|
301
|
+
|├── Account/
|
|
302
|
+
|│ ├── postBlockingCreate.md
|
|
303
|
+
|│ ├── postBlockingDelete.md
|
|
304
|
+
|│ └── ...
|
|
305
|
+
|├── Notes/
|
|
306
|
+
|│ ├── postNotesCreate.md
|
|
307
|
+
|│ └── ...
|
|
308
|
+
|└── ...
|
|
309
|
+
|\`\`\`
|
|
310
|
+
|
|
|
311
|
+
|## How to Override
|
|
312
|
+
|
|
|
313
|
+
|1. Find the endpoint you want to document in its module directory
|
|
314
|
+
|2. Open the markdown file
|
|
315
|
+
|3. Edit the code block under the "## Override" section
|
|
316
|
+
|4. Add your custom documentation (supports markdown)
|
|
317
|
+
|5. Regenerate the code - your custom documentation will be used instead of the default
|
|
318
|
+
|
|
|
319
|
+
|## File Format
|
|
320
|
+
|
|
|
321
|
+
|Each file contains:
|
|
322
|
+
|
|
|
323
|
+
|### Frontmatter
|
|
324
|
+
|- \`endpoint\`: The API endpoint path
|
|
325
|
+
|- \`method\`: HTTP method (GET, POST, etc.)
|
|
326
|
+
|- \`hash\`: Hash of the endpoint for change detection
|
|
327
|
+
|- \`host\`: API host URL
|
|
328
|
+
|- \`version\`: API version
|
|
329
|
+
|- \`operationId\`: OpenAPI operation ID
|
|
330
|
+
|
|
|
331
|
+
|### Default Description
|
|
332
|
+
|The original description from the OpenAPI spec.
|
|
333
|
+
|
|
|
334
|
+
|### Override Section
|
|
335
|
+
|A code block where you can add your custom documentation. If empty, the default description is used.
|
|
336
|
+
|
|
|
337
|
+
|## Example
|
|
338
|
+
|
|
|
339
|
+
|\`\`\`markdown
|
|
340
|
+
|---
|
|
341
|
+
|endpoint: /blocking/create
|
|
342
|
+
|method: POST
|
|
343
|
+
|hash: abc123
|
|
344
|
+
|host: https://misskey.io
|
|
345
|
+
|version: 1.0.0
|
|
346
|
+
|---
|
|
347
|
+
|
|
|
348
|
+
|# blocking/create
|
|
349
|
+
|
|
|
350
|
+
|**Path**: \`/blocking/create\`
|
|
351
|
+
|**Method**: \`POST\`
|
|
352
|
+
|
|
|
353
|
+
|## Default Description
|
|
354
|
+
|
|
|
355
|
+
|No description provided.
|
|
356
|
+
|
|
|
357
|
+
|**Credential required**: *Yes* / **Permission**: *write:blocks*
|
|
358
|
+
|
|
|
359
|
+
|## Override
|
|
360
|
+
|
|
|
361
|
+
|\`\`\`
|
|
362
|
+
|Create a blocking relationship with another user.
|
|
363
|
+
|
|
|
364
|
+
|This endpoint allows you to block a user by their user ID. Once blocked:
|
|
365
|
+
|- The user will not be able to see your posts
|
|
366
|
+
|- You will not see their posts in your timeline
|
|
367
|
+
|- They cannot follow you
|
|
368
|
+
|
|
|
369
|
+
|**Parameters:**
|
|
370
|
+
|- \`userId\`: The ID of the user to block
|
|
371
|
+
|
|
|
372
|
+
|**Example:**
|
|
373
|
+
|\`\`\`typescript
|
|
374
|
+
|await client.blocking.create({ userId: "user123" })
|
|
375
|
+
|\`\`\`
|
|
376
|
+
|\`\`\`
|
|
377
|
+
|\`\`\`
|
|
378
|
+
|
|
|
379
|
+
|## Notes
|
|
380
|
+
|
|
|
381
|
+
|- The hash is used to detect if the endpoint has changed in the OpenAPI spec
|
|
382
|
+
|- If the endpoint changes, you may need to update your override
|
|
383
|
+
|- Empty override blocks (with just \`<!-- Empty - no override -->\`) are ignored
|
|
384
|
+
|`, undefined);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export {
|
|
388
|
+
generateEndpointHash,
|
|
389
|
+
extractCodeBlock,
|
|
390
|
+
parseOverrideMarkdown,
|
|
391
|
+
generateOverrideMarkdown,
|
|
392
|
+
validateOverride,
|
|
393
|
+
readOverrideWithValidation,
|
|
394
|
+
readOverride,
|
|
395
|
+
isFileCustomized,
|
|
396
|
+
generateOverrideFiles,
|
|
397
|
+
generateOverrideReadme,
|
|
398
|
+
}
|
|
399
|
+
/* fs Not a pure module */
|