@f3liz/rescript-autogen-openapi 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.
Files changed (64) hide show
  1. package/LICENSE +339 -0
  2. package/README.md +98 -0
  3. package/lib/es6/src/Codegen.mjs +423 -0
  4. package/lib/es6/src/Types.mjs +20 -0
  5. package/lib/es6/src/core/CodegenUtils.mjs +186 -0
  6. package/lib/es6/src/core/DocOverride.mjs +399 -0
  7. package/lib/es6/src/core/FileSystem.mjs +78 -0
  8. package/lib/es6/src/core/IRBuilder.mjs +201 -0
  9. package/lib/es6/src/core/OpenAPIParser.mjs +168 -0
  10. package/lib/es6/src/core/Pipeline.mjs +150 -0
  11. package/lib/es6/src/core/ReferenceResolver.mjs +41 -0
  12. package/lib/es6/src/core/Result.mjs +378 -0
  13. package/lib/es6/src/core/SchemaIR.mjs +355 -0
  14. package/lib/es6/src/core/SchemaIRParser.mjs +490 -0
  15. package/lib/es6/src/core/SchemaRefResolver.mjs +146 -0
  16. package/lib/es6/src/core/SchemaRegistry.mjs +92 -0
  17. package/lib/es6/src/core/SpecDiffer.mjs +251 -0
  18. package/lib/es6/src/core/SpecMerger.mjs +237 -0
  19. package/lib/es6/src/generators/ComponentSchemaGenerator.mjs +125 -0
  20. package/lib/es6/src/generators/DiffReportGenerator.mjs +155 -0
  21. package/lib/es6/src/generators/EndpointGenerator.mjs +172 -0
  22. package/lib/es6/src/generators/IRToSuryGenerator.mjs +233 -0
  23. package/lib/es6/src/generators/IRToTypeGenerator.mjs +241 -0
  24. package/lib/es6/src/generators/IRToTypeScriptGenerator.mjs +143 -0
  25. package/lib/es6/src/generators/ModuleGenerator.mjs +285 -0
  26. package/lib/es6/src/generators/SchemaCodeGenerator.mjs +77 -0
  27. package/lib/es6/src/generators/ThinWrapperGenerator.mjs +97 -0
  28. package/lib/es6/src/generators/TypeScriptDtsGenerator.mjs +172 -0
  29. package/lib/es6/src/generators/TypeScriptWrapperGenerator.mjs +145 -0
  30. package/lib/es6/src/types/CodegenError.mjs +79 -0
  31. package/lib/es6/src/types/Config.mjs +42 -0
  32. package/lib/es6/src/types/GenerationContext.mjs +24 -0
  33. package/package.json +44 -0
  34. package/rescript.json +20 -0
  35. package/src/Codegen.res +222 -0
  36. package/src/Types.res +195 -0
  37. package/src/core/CodegenUtils.res +130 -0
  38. package/src/core/DocOverride.res +504 -0
  39. package/src/core/FileSystem.res +62 -0
  40. package/src/core/IRBuilder.res +66 -0
  41. package/src/core/OpenAPIParser.res +144 -0
  42. package/src/core/Pipeline.res +51 -0
  43. package/src/core/ReferenceResolver.res +41 -0
  44. package/src/core/Result.res +187 -0
  45. package/src/core/SchemaIR.res +258 -0
  46. package/src/core/SchemaIRParser.res +360 -0
  47. package/src/core/SchemaRefResolver.res +143 -0
  48. package/src/core/SchemaRegistry.res +107 -0
  49. package/src/core/SpecDiffer.res +270 -0
  50. package/src/core/SpecMerger.res +245 -0
  51. package/src/generators/ComponentSchemaGenerator.res +127 -0
  52. package/src/generators/DiffReportGenerator.res +152 -0
  53. package/src/generators/EndpointGenerator.res +172 -0
  54. package/src/generators/IRToSuryGenerator.res +199 -0
  55. package/src/generators/IRToTypeGenerator.res +199 -0
  56. package/src/generators/IRToTypeScriptGenerator.res +72 -0
  57. package/src/generators/ModuleGenerator.res +362 -0
  58. package/src/generators/SchemaCodeGenerator.res +83 -0
  59. package/src/generators/ThinWrapperGenerator.res +124 -0
  60. package/src/generators/TypeScriptDtsGenerator.res +193 -0
  61. package/src/generators/TypeScriptWrapperGenerator.res +166 -0
  62. package/src/types/CodegenError.res +82 -0
  63. package/src/types/Config.res +89 -0
  64. package/src/types/GenerationContext.res +23 -0
@@ -0,0 +1,77 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Text from "@std/text";
4
+ import * as CodegenUtils from "../core/CodegenUtils.mjs";
5
+ import * as OpenAPIParser from "../core/OpenAPIParser.mjs";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as SchemaIRParser from "../core/SchemaIRParser.mjs";
8
+ import * as IRToSuryGenerator from "./IRToSuryGenerator.mjs";
9
+ import * as IRToTypeGenerator from "./IRToTypeGenerator.mjs";
10
+
11
+ function generateTypeCodeAndSchemaCode(name, schema) {
12
+ let match = SchemaIRParser.parseJsonSchema(undefined, schema);
13
+ let ir = match[0];
14
+ let match$1 = IRToTypeGenerator.generateNamedType({
15
+ name: name,
16
+ description: schema.description,
17
+ type_: ir
18
+ }, undefined, undefined, undefined);
19
+ let match$2 = IRToSuryGenerator.generateNamedSchema({
20
+ name: name + `Schema`,
21
+ description: schema.description,
22
+ type_: ir
23
+ }, undefined, undefined, undefined);
24
+ return match$1[0] + `\n\n` + match$2[0];
25
+ }
26
+
27
+ let generateTypeAndSchema = generateTypeCodeAndSchemaCode;
28
+
29
+ function generateComponentSchemas(components) {
30
+ return Stdlib_Option.mapOr(Stdlib_Option.flatMap(components, c => c.schemas), "// No component schemas\n", schemas => {
31
+ let sections = Object.entries(schemas).map(param => generateTypeCodeAndSchemaCode(param[0], param[1])).join("\n\n");
32
+ return `// Component Schemas\n\n` + sections;
33
+ });
34
+ }
35
+
36
+ function generateOperationSchemas(operationId, operation) {
37
+ let generatePart = (suffix, schemaOpt) => Stdlib_Option.mapOr(schemaOpt, "", schema => generateTypeCodeAndSchemaCode(Text.toPascalCase(operationId) + suffix, schema));
38
+ let requestBodySchema = Stdlib_Option.flatMap(Stdlib_Option.flatMap(operation.requestBody, body => body.content["application/json"]), mediaType => mediaType.schema);
39
+ let responses = operation.responses;
40
+ let successResponseSchema = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.orElse(responses["200"], responses["201"]), response => response.content), content => content["application/json"]), mediaType => mediaType.schema);
41
+ return [
42
+ generatePart("Request", requestBodySchema),
43
+ generatePart("Response", successResponseSchema)
44
+ ].filter(code => code !== "").join("\n\n");
45
+ }
46
+
47
+ function generateEndpointModule(path, method, operation) {
48
+ let operationId = OpenAPIParser.getOperationId(path, method, operation);
49
+ let docComment = CodegenUtils.generateDocComment(operation.summary, operation.description, undefined);
50
+ let methodStr = method === "GET" ? "GET" : (
51
+ method === "PUT" ? "PUT" : (
52
+ method === "DELETE" ? "DELETE" : (
53
+ method === "HEAD" ? "HEAD" : (
54
+ method === "POST" ? "POST" : (
55
+ method === "PATCH" ? "PATCH" : "OPTIONS"
56
+ )
57
+ )
58
+ )
59
+ )
60
+ );
61
+ let schemasCode = generateOperationSchemas(operationId, operation);
62
+ return docComment + `module ` + Text.toPascalCase(operationId) + ` = {
63
+ ` + CodegenUtils.indent(schemasCode, 2) + `
64
+
65
+ let endpoint = "` + path + `"
66
+ let method = #` + methodStr + `
67
+ }`;
68
+ }
69
+
70
+ export {
71
+ generateTypeCodeAndSchemaCode,
72
+ generateTypeAndSchema,
73
+ generateComponentSchemas,
74
+ generateOperationSchemas,
75
+ generateEndpointModule,
76
+ }
77
+ /* @std/text Not a pure module */
@@ -0,0 +1,97 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Pipeline from "../core/Pipeline.mjs";
4
+ import * as Text from "@std/text";
5
+ import * as FileSystem from "../core/FileSystem.mjs";
6
+ import * as CodegenUtils from "../core/CodegenUtils.mjs";
7
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
8
+ import * as OpenAPIParser from "../core/OpenAPIParser.mjs";
9
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
10
+
11
+ let clientTypeCode = CodegenUtils.trimMargin(`
12
+ |type client = {
13
+ | baseUrl: string,
14
+ | token: option<string>,
15
+ | fetch: ` + CodegenUtils.fetchTypeSignature + `,
16
+ |}
17
+ |`, undefined);
18
+
19
+ function generateConnectFunction(title) {
20
+ return CodegenUtils.trimMargin(`
21
+ |/** Create a client for ` + title + ` */
22
+ |let connect = (~baseUrl: string, ~token: option<string>=?, ~fetch: ` + CodegenUtils.fetchTypeSignature + `, ()): client => {
23
+ | baseUrl,
24
+ | token,
25
+ | fetch,
26
+ |}
27
+ |`, undefined);
28
+ }
29
+
30
+ function generateWrapperFunction(endpoint, generatedModuleName) {
31
+ let operationName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
32
+ let hasRequestBody = Stdlib_Option.isSome(endpoint.requestBody);
33
+ let docComment = Stdlib_Option.mapOr(endpoint.summary, "", summary => {
34
+ let descriptionPart = Stdlib_Option.mapOr(endpoint.description, "", description => {
35
+ if (description === summary) {
36
+ return "";
37
+ } else {
38
+ return " - " + description;
39
+ }
40
+ });
41
+ return ` /** ` + summary + descriptionPart + ` */\n`;
42
+ });
43
+ let signature = hasRequestBody ? `let ` + operationName + ` = (request: ` + generatedModuleName + `.` + operationName + `Request, ~client: client)` : `let ` + operationName + ` = (~client: client)`;
44
+ let callArguments = hasRequestBody ? "~body=request, " : "";
45
+ return docComment + ` ` + signature + `: promise<` + generatedModuleName + `.` + operationName + `Response> =>
46
+ ` + generatedModuleName + `.` + operationName + `(` + callArguments + `~fetch=client.fetch)`;
47
+ }
48
+
49
+ function generateWrapper(spec, endpoints, extensionEndpointsOpt, outputDir, wrapperModuleNameOpt, generatedModulePrefixOpt, baseModulePrefixOpt) {
50
+ let extensionEndpoints = extensionEndpointsOpt !== undefined ? extensionEndpointsOpt : [];
51
+ let wrapperModuleName = wrapperModuleNameOpt !== undefined ? wrapperModuleNameOpt : "Wrapper";
52
+ let generatedModulePrefix = generatedModulePrefixOpt !== undefined ? generatedModulePrefixOpt : "";
53
+ let baseModulePrefix = baseModulePrefixOpt !== undefined ? baseModulePrefixOpt : "";
54
+ let extensionOperationIds = Stdlib_Array.reduce(extensionEndpoints, {}, (acc, endpoint) => {
55
+ let name = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
56
+ acc[name] = true;
57
+ return acc;
58
+ });
59
+ let hasExtensions = extensionEndpoints.length !== 0;
60
+ let allEndpoints = (
61
+ hasExtensions ? endpoints.filter(endpoint => {
62
+ let name = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
63
+ return !(name in extensionOperationIds);
64
+ }) : endpoints
65
+ ).concat(extensionEndpoints);
66
+ let endpointsByTag = OpenAPIParser.groupByTag(allEndpoints);
67
+ let modulesCode = Object.keys(endpointsByTag).map(tag => {
68
+ let moduleName = Text.toPascalCase(tag);
69
+ let wrapperFunctions = Stdlib_Option.getOr(endpointsByTag[tag], []).map(endpoint => {
70
+ let operationName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
71
+ let isExtension = hasExtensions && operationName in extensionOperationIds;
72
+ let prefix = !isExtension && baseModulePrefix !== "" ? baseModulePrefix : generatedModulePrefix;
73
+ let targetModuleName = prefix !== "" ? prefix + moduleName : moduleName;
74
+ return generateWrapperFunction(endpoint, targetModuleName);
75
+ }).join("\n\n");
76
+ return `module ` + moduleName + ` = {\n` + wrapperFunctions + `\n}`;
77
+ }).join("\n\n");
78
+ let fileContent = `// Generated thin wrapper
79
+
80
+ ` + clientTypeCode + `
81
+
82
+ ` + generateConnectFunction(spec.info.title) + `
83
+
84
+ ` + modulesCode;
85
+ return Pipeline.fromFilesAndWarnings([{
86
+ path: FileSystem.makePath(outputDir, wrapperModuleName + `.res`),
87
+ content: fileContent
88
+ }], []);
89
+ }
90
+
91
+ export {
92
+ clientTypeCode,
93
+ generateConnectFunction,
94
+ generateWrapperFunction,
95
+ generateWrapper,
96
+ }
97
+ /* clientTypeCode Not a pure module */
@@ -0,0 +1,172 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Text from "@std/text";
4
+ import * as FileSystem from "../core/FileSystem.mjs";
5
+ import * as CodegenUtils from "../core/CodegenUtils.mjs";
6
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
7
+ import * as OpenAPIParser from "../core/OpenAPIParser.mjs";
8
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
+ import * as SchemaIRParser from "../core/SchemaIRParser.mjs";
10
+ import * as IRToTypeScriptGenerator from "./IRToTypeScriptGenerator.mjs";
11
+
12
+ function getFirstJsonSchema(contentDict) {
13
+ return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Object.keys(contentDict)[0], contentType => contentDict[contentType]), mediaType => mediaType.schema);
14
+ }
15
+
16
+ function generateTypeScriptType(name, description, schema) {
17
+ let match = SchemaIRParser.parseJsonSchema(undefined, schema);
18
+ return IRToTypeScriptGenerator.generateNamedType({
19
+ name: name,
20
+ description: description,
21
+ type_: match[0]
22
+ });
23
+ }
24
+
25
+ function generateRequestInterface(endpoint, functionName) {
26
+ let requestTypeName = Text.toPascalCase(functionName) + `Request`;
27
+ return Stdlib_Option.flatMap(endpoint.requestBody, body => Stdlib_Option.map(getFirstJsonSchema(body.content), schema => generateTypeScriptType(requestTypeName, body.description, schema)));
28
+ }
29
+
30
+ function generateResponseInterface(endpoint, functionName) {
31
+ let responseTypeName = Text.toPascalCase(functionName) + `Response`;
32
+ let successCodes = [
33
+ "200",
34
+ "201",
35
+ "202",
36
+ "204"
37
+ ];
38
+ let successResponse = Stdlib_Array.filterMap(successCodes, code => endpoint.responses[code])[0];
39
+ return Stdlib_Option.getOr(Stdlib_Option.flatMap(successResponse, response => Stdlib_Option.map(Stdlib_Option.flatMap(response.content, getFirstJsonSchema), schema => generateTypeScriptType(responseTypeName, response.description, schema))), `export type ` + responseTypeName + ` = void;`);
40
+ }
41
+
42
+ function generateMethodSignature(endpoint, functionName) {
43
+ let params = Stdlib_Option.isSome(endpoint.requestBody) ? `client: MisskeyClient, request: ` + Text.toPascalCase(functionName) + `Request` : "client: MisskeyClient";
44
+ let docLines = Stdlib_Option.mapOr(endpoint.summary, [], summary => {
45
+ let lines = [
46
+ " /**",
47
+ ` * ` + summary
48
+ ];
49
+ Stdlib_Option.forEach(endpoint.description, description => {
50
+ if (description !== summary) {
51
+ lines.push(` * ` + description);
52
+ return;
53
+ }
54
+ });
55
+ lines.push(" */");
56
+ return lines;
57
+ });
58
+ let code = `
59
+ |` + docLines.join("\n") + `
60
+ | ` + functionName + `(` + params + `): Promise<` + Text.toPascalCase(functionName) + `Response>;`;
61
+ return CodegenUtils.trimMargin(code, undefined);
62
+ }
63
+
64
+ function generateModuleDts(moduleName, endpoints) {
65
+ let interfaces = endpoints.map(endpoint => {
66
+ let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
67
+ let requestPart = Stdlib_Option.getOr(generateRequestInterface(endpoint, functionName), "");
68
+ let responsePart = generateResponseInterface(endpoint, functionName);
69
+ return [
70
+ requestPart,
71
+ responsePart
72
+ ].filter(s => s !== "").join("\n");
73
+ }).join("\n\n");
74
+ let methodSignatures = endpoints.map(endpoint => generateMethodSignature(endpoint, CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method))).join("\n");
75
+ let header = CodegenUtils.trimMargin(`
76
+ |// TypeScript definitions for ` + moduleName + `
77
+ |// Generated by @f3liz/rescript-autogen-openapi
78
+ |// DO NOT EDIT
79
+ |
80
+ |import { MisskeyClient } from './index';
81
+ |import * as ComponentSchemas from './ComponentSchemas';
82
+ |`, undefined);
83
+ return CodegenUtils.trimMargin(`
84
+ |` + header + `
85
+ |
86
+ |` + interfaces + `
87
+ |
88
+ |export interface ` + moduleName + `Module {
89
+ |` + methodSignatures + `
90
+ |}
91
+ |
92
+ |export const ` + moduleName + `: ` + moduleName + `Module;
93
+ |`, undefined);
94
+ }
95
+
96
+ function generateComponentSchemasDts(schemas) {
97
+ let content = Object.entries(schemas).map(param => {
98
+ let schema = param[1];
99
+ return generateTypeScriptType(param[0], schema.description, schema);
100
+ }).join("\n\n");
101
+ return CodegenUtils.trimMargin(`
102
+ |// TypeScript definitions for ComponentSchemas
103
+ |// Generated by @f3liz/rescript-autogen-openapi
104
+ |// DO NOT EDIT
105
+ |
106
+ |` + content + `
107
+ |`, undefined);
108
+ }
109
+
110
+ function generateIndexDts(moduleNames) {
111
+ let imports = moduleNames.map(m => `import { ` + m + `Module } from './` + m + `';`).join("\n");
112
+ let $$exports = moduleNames.map(m => `export const ` + m + `: ` + m + `Module;`).join("\n");
113
+ return CodegenUtils.trimMargin(`
114
+ |// TypeScript definitions
115
+ |// Generated by @f3liz/rescript-autogen-openapi
116
+ |// DO NOT EDIT
117
+ |
118
+ |` + imports + `
119
+ |
120
+ |export class MisskeyClient {
121
+ | constructor(baseUrl: string, token?: string);
122
+ | readonly baseUrl: string;
123
+ | readonly token?: string;
124
+ |}
125
+ |
126
+ |` + $$exports + `
127
+ |`, undefined);
128
+ }
129
+
130
+ function generate(spec, endpoints, outputDir) {
131
+ let endpointsByTag = OpenAPIParser.groupByTag(endpoints);
132
+ let moduleNames = [];
133
+ let files = Stdlib_Array.filterMap(Object.entries(endpointsByTag), param => {
134
+ let tagEndpoints = param[1];
135
+ if (tagEndpoints.length === 0) {
136
+ return;
137
+ }
138
+ let name = Text.toPascalCase(param[0]);
139
+ moduleNames.push(name);
140
+ return {
141
+ path: FileSystem.makePath(outputDir, `types/` + name + `.d.ts`),
142
+ content: generateModuleDts(name, tagEndpoints)
143
+ };
144
+ });
145
+ Stdlib_Option.forEach(Stdlib_Option.flatMap(spec.components, c => c.schemas), schemas => {
146
+ files.push({
147
+ path: FileSystem.makePath(outputDir, "types/ComponentSchemas.d.ts"),
148
+ content: generateComponentSchemasDts(schemas)
149
+ });
150
+ });
151
+ files.push({
152
+ path: FileSystem.makePath(outputDir, "types/index.d.ts"),
153
+ content: generateIndexDts(moduleNames)
154
+ });
155
+ return {
156
+ files: files,
157
+ warnings: []
158
+ };
159
+ }
160
+
161
+ export {
162
+ getFirstJsonSchema,
163
+ generateTypeScriptType,
164
+ generateRequestInterface,
165
+ generateResponseInterface,
166
+ generateMethodSignature,
167
+ generateModuleDts,
168
+ generateComponentSchemasDts,
169
+ generateIndexDts,
170
+ generate,
171
+ }
172
+ /* @std/text Not a pure module */
@@ -0,0 +1,145 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Pipeline from "../core/Pipeline.mjs";
4
+ import * as Text from "@std/text";
5
+ import * as FileSystem from "../core/FileSystem.mjs";
6
+ import * as CodegenUtils from "../core/CodegenUtils.mjs";
7
+ import * as OpenAPIParser from "../core/OpenAPIParser.mjs";
8
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
+
10
+ let misskeyClientJsCode = CodegenUtils.trimMargin(`
11
+ |export class MisskeyClient {
12
+ | constructor(baseUrl, token) {
13
+ | this.baseUrl = baseUrl;
14
+ | this.token = token;
15
+ | }
16
+ |
17
+ | async _fetch(url, method, body) {
18
+ | const headers = { 'Content-Type': 'application/json' };
19
+ | if (this.token) {
20
+ | headers['Authorization'] = \`Bearer \${this.token}\`;
21
+ | }
22
+ | const response = await fetch(this.baseUrl + url, {
23
+ | method,
24
+ | headers,
25
+ | body: body ? JSON.stringify(body) : undefined,
26
+ | });
27
+ | return response.json();
28
+ | }
29
+ |}`, undefined);
30
+
31
+ function generateWrapperMjs(endpoints, generatedModulePath) {
32
+ let endpointsByTag = OpenAPIParser.groupByTag(endpoints);
33
+ let tags = Object.keys(endpointsByTag);
34
+ let imports = tags.map(tag => {
35
+ let moduleName = Text.toPascalCase(tag);
36
+ return `import * as ` + moduleName + ` from '` + generatedModulePath + `/` + moduleName + `.mjs';`;
37
+ }).join("\n");
38
+ let wrappers = tags.map(tag => {
39
+ let moduleName = Text.toPascalCase(tag);
40
+ let methods = Stdlib_Option.getOr(endpointsByTag[tag], []).map(endpoint => {
41
+ let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
42
+ let hasRequestBody = Stdlib_Option.isSome(endpoint.requestBody);
43
+ let bodyArg = hasRequestBody ? "body: request, " : "";
44
+ return `
45
+ | async ` + functionName + `(client` + (
46
+ hasRequestBody ? ", request" : ""
47
+ ) + `) {
48
+ | return ` + moduleName + `.` + functionName + `({
49
+ | ` + bodyArg + `fetch: (url, method, body) => client._fetch(url, method, body)
50
+ | });
51
+ | },`;
52
+ }).join("\n");
53
+ return `
54
+ |export const ` + moduleName + ` = {
55
+ |` + methods + `
56
+ |};`;
57
+ }).join("\n\n");
58
+ return CodegenUtils.trimMargin(`
59
+ |// Generated wrapper
60
+ |` + imports + `
61
+ |
62
+ |` + misskeyClientJsCode + `
63
+ |
64
+ |` + wrappers + `
65
+ |`, undefined);
66
+ }
67
+
68
+ function generateWrapperDts(endpoints) {
69
+ let endpointsByTag = OpenAPIParser.groupByTag(endpoints);
70
+ let tags = Object.keys(endpointsByTag);
71
+ let imports = tags.map(tag => {
72
+ let moduleName = Text.toPascalCase(tag);
73
+ let typesToImport = Stdlib_Option.getOr(endpointsByTag[tag], []).flatMap(endpoint => {
74
+ let pascalName = Text.toPascalCase(CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method));
75
+ if (Stdlib_Option.isSome(endpoint.requestBody)) {
76
+ return [
77
+ ` ` + pascalName + `Request,`,
78
+ ` ` + pascalName + `Response,`
79
+ ];
80
+ } else {
81
+ return [` ` + pascalName + `Response,`];
82
+ }
83
+ }).join("\n");
84
+ return `import type {
85
+ ` + typesToImport + `
86
+ } from '../types/` + moduleName + `.d.ts';`;
87
+ }).join("\n");
88
+ let namespaces = tags.map(tag => {
89
+ let moduleName = Text.toPascalCase(tag);
90
+ let functions = Stdlib_Option.getOr(endpointsByTag[tag], []).map(endpoint => {
91
+ let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
92
+ let pascalName = Text.toPascalCase(functionName);
93
+ let docComment = Stdlib_Option.mapOr(endpoint.summary, "", summary => {
94
+ let descriptionPart = Stdlib_Option.mapOr(endpoint.description, "", description => {
95
+ if (description === summary) {
96
+ return "";
97
+ } else {
98
+ return " - " + description;
99
+ }
100
+ });
101
+ return ` /** ` + summary + descriptionPart + ` */\n`;
102
+ });
103
+ let requestParam = Stdlib_Option.isSome(endpoint.requestBody) ? `, request: ` + pascalName + `Request` : "";
104
+ return docComment + ` export function ` + functionName + `(client: MisskeyClient` + requestParam + `): Promise<` + pascalName + `Response>;`;
105
+ }).join("\n");
106
+ return `
107
+ |export namespace ` + moduleName + ` {
108
+ |` + functions + `
109
+ |}`;
110
+ }).join("\n\n");
111
+ return CodegenUtils.trimMargin(`
112
+ |// Generated TypeScript definitions for wrapper
113
+ |` + imports + `
114
+ |
115
+ |export class MisskeyClient {
116
+ | constructor(baseUrl: string, token?: string);
117
+ | readonly baseUrl: string;
118
+ | readonly token?: string;
119
+ |}
120
+ |
121
+ |` + namespaces + `
122
+ |`, undefined);
123
+ }
124
+
125
+ function generate(endpoints, outputDir, generatedModulePathOpt) {
126
+ let generatedModulePath = generatedModulePathOpt !== undefined ? generatedModulePathOpt : "../generated";
127
+ return Pipeline.fromFilesAndWarnings([
128
+ {
129
+ path: FileSystem.makePath(outputDir, "wrapper/index.mjs"),
130
+ content: generateWrapperMjs(endpoints, generatedModulePath)
131
+ },
132
+ {
133
+ path: FileSystem.makePath(outputDir, "wrapper/index.d.ts"),
134
+ content: generateWrapperDts(endpoints)
135
+ }
136
+ ], []);
137
+ }
138
+
139
+ export {
140
+ misskeyClientJsCode,
141
+ generateWrapperMjs,
142
+ generateWrapperDts,
143
+ generate,
144
+ }
145
+ /* misskeyClientJsCode Not a pure module */
@@ -0,0 +1,79 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function toString(w) {
5
+ switch (w.TAG) {
6
+ case "FallbackToJson" :
7
+ let context = w.context;
8
+ return `⚠️ Falling back to JSON.t at '` + context.path + `' (` + context.operation + `): ` + w.reason;
9
+ case "UnsupportedFeature" :
10
+ return `⚠️ Unsupported feature '` + w.feature + `' at '` + w.location + `', using fallback: ` + w.fallback;
11
+ case "DepthLimitReached" :
12
+ return `⚠️ Depth limit ` + w.depth.toString() + ` reached at '` + w.path + `', using simplified type`;
13
+ case "MissingSchema" :
14
+ return `⚠️ Schema reference '` + w.ref + `' not found at '` + w.location + `'`;
15
+ case "IntersectionNotFullySupported" :
16
+ return `⚠️ Intersection type at '` + w.location + `' not fully supported: ` + w.note;
17
+ case "ComplexUnionSimplified" :
18
+ return `⚠️ Complex union at '` + w.location + `' simplified (types: ` + w.types + `)`;
19
+ }
20
+ }
21
+
22
+ function print(warnings) {
23
+ if (warnings.length !== 0) {
24
+ console.log("\n⚠️ Warnings:");
25
+ warnings.forEach(w => {
26
+ console.log(toString(w));
27
+ });
28
+ return;
29
+ }
30
+ }
31
+
32
+ let Warning = {
33
+ toString: toString,
34
+ print: print
35
+ };
36
+
37
+ function toString$1(e) {
38
+ switch (e.TAG) {
39
+ case "SpecResolutionError" :
40
+ return `Failed to resolve spec from '` + e.url + `': ` + e.message;
41
+ case "SchemaParseError" :
42
+ let context = e.context;
43
+ return `Failed to parse schema at '` + context.path + `' (` + context.operation + `): ` + e.reason;
44
+ case "ReferenceError" :
45
+ let context$1 = e.context;
46
+ return `Failed to resolve reference '` + e.ref + `' at '` + context$1.path + `' (` + context$1.operation + `)`;
47
+ case "ValidationError" :
48
+ return `Validation failed for schema '` + e.schema + `': ` + e.issues.join(", ");
49
+ case "CircularSchemaError" :
50
+ return `Circular schema detected for '` + e.ref + `' at depth ` + e.depth.toString() + ` (path: ` + e.path + `)`;
51
+ case "FileWriteError" :
52
+ return `Failed to write file '` + e.filePath + `': ` + e.message;
53
+ case "InvalidConfigError" :
54
+ return `Invalid configuration for field '` + e.field + `': ` + e.message;
55
+ case "UnknownError" :
56
+ let context$2 = e.context;
57
+ let message = e.message;
58
+ if (context$2 !== undefined) {
59
+ return `Unknown error at '` + context$2.path + `' (` + context$2.operation + `): ` + message;
60
+ } else {
61
+ return `Unknown error: ` + message;
62
+ }
63
+ }
64
+ }
65
+
66
+ function makeContext(path, operation, schema, param) {
67
+ return {
68
+ path: path,
69
+ operation: operation,
70
+ schema: schema
71
+ };
72
+ }
73
+
74
+ export {
75
+ Warning,
76
+ toString$1 as toString,
77
+ makeContext,
78
+ }
79
+ /* No side effect */
@@ -0,0 +1,42 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function make(specPath, outputDir, strategyOpt, modulePerTagOpt, includeTags, excludeTags, generateDiffReportOpt, breakingChangeHandlingOpt, forkSpecs, generateDocOverrides, docOverrideDir, targets, dtsOutputDir, wrapperOutputDir, baseInstanceName, baseModulePrefix, param) {
5
+ let strategy = strategyOpt !== undefined ? strategyOpt : "SharedBase";
6
+ let modulePerTag = modulePerTagOpt !== undefined ? modulePerTagOpt : true;
7
+ let generateDiffReport = generateDiffReportOpt !== undefined ? generateDiffReportOpt : true;
8
+ let breakingChangeHandling = breakingChangeHandlingOpt !== undefined ? breakingChangeHandlingOpt : "Warn";
9
+ return {
10
+ specPath: specPath,
11
+ forkSpecs: forkSpecs,
12
+ outputDir: outputDir,
13
+ strategy: strategy,
14
+ modulePerTag: modulePerTag,
15
+ includeTags: includeTags,
16
+ excludeTags: excludeTags,
17
+ generateDiffReport: generateDiffReport,
18
+ breakingChangeHandling: breakingChangeHandling,
19
+ generateDocOverrides: generateDocOverrides,
20
+ docOverrideDir: docOverrideDir,
21
+ targets: targets,
22
+ dtsOutputDir: dtsOutputDir,
23
+ wrapperOutputDir: wrapperOutputDir,
24
+ baseInstanceName: baseInstanceName,
25
+ baseModulePrefix: baseModulePrefix
26
+ };
27
+ }
28
+
29
+ function defaultTargets() {
30
+ return {
31
+ rescriptApi: true,
32
+ rescriptWrapper: false,
33
+ typescriptDts: false,
34
+ typescriptWrapper: false
35
+ };
36
+ }
37
+
38
+ export {
39
+ make,
40
+ defaultTargets,
41
+ }
42
+ /* No side effect */
@@ -0,0 +1,24 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function make(path, insideComponentSchemasOpt, availableSchemas, modulePrefixOpt, param) {
5
+ let insideComponentSchemas = insideComponentSchemasOpt !== undefined ? insideComponentSchemasOpt : false;
6
+ let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
7
+ return {
8
+ warnings: [],
9
+ path: path,
10
+ insideComponentSchemas: insideComponentSchemas,
11
+ availableSchemas: availableSchemas,
12
+ modulePrefix: modulePrefix
13
+ };
14
+ }
15
+
16
+ function addWarning(ctx, warning) {
17
+ ctx.warnings.push(warning);
18
+ }
19
+
20
+ export {
21
+ make,
22
+ addWarning,
23
+ }
24
+ /* No side effect */
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@f3liz/rescript-autogen-openapi",
3
+ "version": "0.1.0",
4
+ "description": "Generate ReScript code with Sury schemas from OpenAPI 3.1 specs. Supports multiple forks with diff/merge capabilities.",
5
+ "keywords": [
6
+ "rescript",
7
+ "openapi",
8
+ "codegen",
9
+ "sury",
10
+ "schema",
11
+ "misskey",
12
+ "api",
13
+ "generator"
14
+ ],
15
+ "author": "",
16
+ "license": "MPL-2.0",
17
+ "files": [
18
+ "src/**/*.res",
19
+ "lib/es6/**/*.mjs",
20
+ "rescript.json",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "build": "rescript",
26
+ "clean": "rescript clean",
27
+ "watch": "rescript build -w",
28
+ "test": "node --test"
29
+ },
30
+ "main": "lib/es6/src/Codegen.mjs",
31
+ "type": "module",
32
+ "dependencies": {
33
+ "@readme/openapi-parser": "^5.5.0",
34
+ "@std/path": "npm:@jsr/std__path@^1.1.4",
35
+ "@std/text": "npm:@jsr/std__text@^1.0.17",
36
+ "sury": "11.0.0-alpha.4"
37
+ },
38
+ "devDependencies": {
39
+ "rescript": "^12.1.0"
40
+ },
41
+ "peerDependencies": {
42
+ "rescript": "^12.0.0"
43
+ }
44
+ }