@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.
- package/LICENSE +339 -0
- package/README.md +98 -0
- package/lib/es6/src/Codegen.mjs +423 -0
- package/lib/es6/src/Types.mjs +20 -0
- package/lib/es6/src/core/CodegenUtils.mjs +186 -0
- package/lib/es6/src/core/DocOverride.mjs +399 -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.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 +355 -0
- package/lib/es6/src/core/SchemaIRParser.mjs +490 -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 +125 -0
- package/lib/es6/src/generators/DiffReportGenerator.mjs +155 -0
- package/lib/es6/src/generators/EndpointGenerator.mjs +172 -0
- package/lib/es6/src/generators/IRToSuryGenerator.mjs +233 -0
- package/lib/es6/src/generators/IRToTypeGenerator.mjs +241 -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.mjs +79 -0
- package/lib/es6/src/types/Config.mjs +42 -0
- package/lib/es6/src/types/GenerationContext.mjs +24 -0
- package/package.json +44 -0
- package/rescript.json +20 -0
- package/src/Codegen.res +222 -0
- package/src/Types.res +195 -0
- package/src/core/CodegenUtils.res +130 -0
- package/src/core/DocOverride.res +504 -0
- package/src/core/FileSystem.res +62 -0
- package/src/core/IRBuilder.res +66 -0
- package/src/core/OpenAPIParser.res +144 -0
- package/src/core/Pipeline.res +51 -0
- package/src/core/ReferenceResolver.res +41 -0
- package/src/core/Result.res +187 -0
- package/src/core/SchemaIR.res +258 -0
- package/src/core/SchemaIRParser.res +360 -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 +127 -0
- package/src/generators/DiffReportGenerator.res +152 -0
- package/src/generators/EndpointGenerator.res +172 -0
- package/src/generators/IRToSuryGenerator.res +199 -0
- package/src/generators/IRToTypeGenerator.res +199 -0
- package/src/generators/IRToTypeScriptGenerator.res +72 -0
- package/src/generators/ModuleGenerator.res +362 -0
- package/src/generators/SchemaCodeGenerator.res +83 -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 +82 -0
- package/src/types/Config.res +89 -0
- package/src/types/GenerationContext.res +23 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as SpecDiffer from "../core/SpecDiffer.mjs";
|
|
4
|
+
import * as CodegenUtils from "../core/CodegenUtils.mjs";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
7
|
+
|
|
8
|
+
function formatEndpointName(endpoint) {
|
|
9
|
+
let methodPart = endpoint.method.toUpperCase();
|
|
10
|
+
let operationIdPart = Stdlib_Option.mapOr(endpoint.operationId, "", id => ` (` + id + `)`);
|
|
11
|
+
return methodPart + ` ` + endpoint.path + operationIdPart;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function formatTags(tags) {
|
|
15
|
+
return Stdlib_Option.mapOr(tags, "", tagList => {
|
|
16
|
+
if (tagList.length === 0) {
|
|
17
|
+
return "";
|
|
18
|
+
} else {
|
|
19
|
+
return ` [` + tagList.join(", ") + `]`;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function generateMarkdownReport(diff, baseName, forkName) {
|
|
25
|
+
let generateSection = (title, items, formatter) => {
|
|
26
|
+
if (items.length === 0) {
|
|
27
|
+
return "";
|
|
28
|
+
} else {
|
|
29
|
+
return `\n### ` + title + `\n\n` + items.map(formatter).join("\n") + `\n`;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
let totalChanges = SpecDiffer.countChanges(diff);
|
|
33
|
+
let breakingChangesText = SpecDiffer.hasBreakingChanges(diff) ? "⚠️ Yes" : "✓ No";
|
|
34
|
+
let summaryLines = [
|
|
35
|
+
`- **Total Changes**: ` + totalChanges.toString(),
|
|
36
|
+
`- **Added Endpoints**: ` + diff.addedEndpoints.length.toString(),
|
|
37
|
+
`- **Removed Endpoints**: ` + diff.removedEndpoints.length.toString(),
|
|
38
|
+
`- **Modified Endpoints**: ` + diff.modifiedEndpoints.length.toString(),
|
|
39
|
+
`- **Added Schemas**: ` + diff.addedSchemas.length.toString(),
|
|
40
|
+
`- **Removed Schemas**: ` + diff.removedSchemas.length.toString(),
|
|
41
|
+
`- **Modified Schemas**: ` + diff.modifiedSchemas.length.toString(),
|
|
42
|
+
`- **Breaking Changes**: ` + breakingChangesText
|
|
43
|
+
].join("\n");
|
|
44
|
+
let reportParts = [
|
|
45
|
+
`# API Diff Report: ` + baseName + ` → ` + forkName + `\n\n## Summary\n\n` + summaryLines,
|
|
46
|
+
generateSection("Added Endpoints", diff.addedEndpoints, endpoint => {
|
|
47
|
+
let endpointName = formatEndpointName(endpoint);
|
|
48
|
+
let tags = formatTags(endpoint.tags);
|
|
49
|
+
let summary = Stdlib_Option.mapOr(endpoint.summary, "", summary => `\n ` + summary);
|
|
50
|
+
return `- **` + endpointName + `**` + tags + summary;
|
|
51
|
+
}),
|
|
52
|
+
generateSection("Removed Endpoints", diff.removedEndpoints, endpoint => {
|
|
53
|
+
let endpointName = formatEndpointName(endpoint);
|
|
54
|
+
let tags = formatTags(endpoint.tags);
|
|
55
|
+
return `- **` + endpointName + `**` + tags;
|
|
56
|
+
}),
|
|
57
|
+
generateSection("Modified Endpoints", diff.modifiedEndpoints, endpointDiff => {
|
|
58
|
+
let methodPart = endpointDiff.method.toUpperCase();
|
|
59
|
+
let breakingText = endpointDiff.breakingChange ? " **⚠️ BREAKING**" : "";
|
|
60
|
+
let changes = [
|
|
61
|
+
endpointDiff.requestBodyChanged ? "body" : "",
|
|
62
|
+
endpointDiff.responseChanged ? "response" : ""
|
|
63
|
+
].filter(x => x !== "").join(", ");
|
|
64
|
+
return `- **` + methodPart + ` ` + endpointDiff.path + `**` + breakingText + `: Changed ` + changes;
|
|
65
|
+
}),
|
|
66
|
+
generateSection("Added Schemas", diff.addedSchemas, schemaName => `- \`` + schemaName + `\``),
|
|
67
|
+
generateSection("Removed Schemas", diff.removedSchemas, schemaName => `- \`` + schemaName + `\``),
|
|
68
|
+
generateSection("Modified Schemas", diff.modifiedSchemas, schemaDiff => {
|
|
69
|
+
let breakingText = schemaDiff.breakingChange ? " **⚠️ BREAKING**" : "";
|
|
70
|
+
return `- \`` + schemaDiff.name + `\`` + breakingText;
|
|
71
|
+
}),
|
|
72
|
+
`\n---\n*Generated on ` + new Date().toISOString() + `*`
|
|
73
|
+
];
|
|
74
|
+
return reportParts.filter(part => part !== "").join("\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function generateCompactSummary(diff) {
|
|
78
|
+
let totalChanges = SpecDiffer.countChanges(diff);
|
|
79
|
+
let addedCount = diff.addedEndpoints.length;
|
|
80
|
+
let removedCount = diff.removedEndpoints.length;
|
|
81
|
+
let modifiedCount = diff.modifiedEndpoints.length;
|
|
82
|
+
let breakingText = SpecDiffer.hasBreakingChanges(diff) ? " (BREAKING)" : "";
|
|
83
|
+
return `Found ` + totalChanges.toString() + ` changes: +` + addedCount.toString() + ` -` + removedCount.toString() + ` ~` + modifiedCount.toString() + ` endpoints` + breakingText;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function generateMergeReport(stats, baseName, forkName) {
|
|
87
|
+
let sharedEndpoints = stats.sharedEndpointCount.toString();
|
|
88
|
+
let sharedSchemas = stats.sharedSchemaCount.toString();
|
|
89
|
+
let extensionEndpoints = stats.forkExtensionCount.toString();
|
|
90
|
+
let extensionSchemas = stats.forkSchemaCount.toString();
|
|
91
|
+
return CodegenUtils.trimMargin(`
|
|
92
|
+
|# Merge Report: ` + baseName + ` + ` + forkName + `
|
|
93
|
+
|
|
|
94
|
+
|## Shared Code
|
|
95
|
+
|
|
|
96
|
+
|- **Shared Endpoints**: ` + sharedEndpoints + `
|
|
97
|
+
|- **Shared Schemas**: ` + sharedSchemas + `
|
|
98
|
+
|
|
|
99
|
+
|## ` + forkName + ` Extensions
|
|
100
|
+
|
|
|
101
|
+
|- **Extension Endpoints**: ` + extensionEndpoints + `
|
|
102
|
+
|- **Extension Schemas**: ` + extensionSchemas + `
|
|
103
|
+
|
|
|
104
|
+
|## Summary
|
|
105
|
+
|
|
|
106
|
+
|The shared base contains ` + sharedEndpoints + ` endpoints and ` + sharedSchemas + ` schemas.
|
|
107
|
+
|
|
|
108
|
+
|` + forkName + ` adds ` + extensionEndpoints + ` endpoints and ` + extensionSchemas + ` schemas.
|
|
109
|
+
|
|
|
110
|
+
|---
|
|
111
|
+
|*Generated on ` + new Date().toISOString() + `*
|
|
112
|
+
|`, undefined);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function generateEndpointsByTagReport(endpoints) {
|
|
116
|
+
let endpointsByTag = {};
|
|
117
|
+
let untaggedEndpoints = [];
|
|
118
|
+
endpoints.forEach(endpoint => {
|
|
119
|
+
let tags = endpoint.tags;
|
|
120
|
+
if (tags !== undefined && tags.length !== 0) {
|
|
121
|
+
tags.forEach(tag => {
|
|
122
|
+
let existing = Stdlib_Option.getOr(endpointsByTag[tag], []);
|
|
123
|
+
existing.push(endpoint);
|
|
124
|
+
endpointsByTag[tag] = existing;
|
|
125
|
+
});
|
|
126
|
+
} else {
|
|
127
|
+
untaggedEndpoints.push(endpoint);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
let tagSections = Object.keys(endpointsByTag).toSorted(Primitive_string.compare).map(tag => {
|
|
131
|
+
let tagEndpoints = Stdlib_Option.getOr(endpointsByTag[tag], []);
|
|
132
|
+
let count = tagEndpoints.length.toString();
|
|
133
|
+
let endpointList = tagEndpoints.map(endpoint => `- ` + formatEndpointName(endpoint)).join("\n");
|
|
134
|
+
return `### ` + tag + ` (` + count + `)\n\n` + endpointList;
|
|
135
|
+
}).join("\n\n");
|
|
136
|
+
let untaggedSection;
|
|
137
|
+
if (untaggedEndpoints.length !== 0) {
|
|
138
|
+
let count = untaggedEndpoints.length.toString();
|
|
139
|
+
let endpointList = untaggedEndpoints.map(endpoint => `- ` + formatEndpointName(endpoint)).join("\n");
|
|
140
|
+
untaggedSection = `\n\n### Untagged (` + count + `)\n\n` + endpointList;
|
|
141
|
+
} else {
|
|
142
|
+
untaggedSection = "";
|
|
143
|
+
}
|
|
144
|
+
return `## Endpoints by Tag\n\n` + tagSections + untaggedSection;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
formatEndpointName,
|
|
149
|
+
formatTags,
|
|
150
|
+
generateMarkdownReport,
|
|
151
|
+
generateCompactSummary,
|
|
152
|
+
generateMergeReport,
|
|
153
|
+
generateEndpointsByTagReport,
|
|
154
|
+
}
|
|
155
|
+
/* CodegenUtils 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 DocOverride from "../core/DocOverride.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 Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as SchemaIRParser from "../core/SchemaIRParser.mjs";
|
|
9
|
+
import * as IRToSuryGenerator from "./IRToSuryGenerator.mjs";
|
|
10
|
+
import * as IRToTypeGenerator from "./IRToTypeGenerator.mjs";
|
|
11
|
+
|
|
12
|
+
function getJsonSchemaFromRequestBody(requestBody) {
|
|
13
|
+
return Stdlib_Option.flatMap(requestBody, body => Stdlib_Option.flatMap(Object.entries(body.content)[0], param => param[1].schema));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function generateTypeCodeAndSchemaCode(jsonSchema, typeName, schemaName, modulePrefixOpt) {
|
|
17
|
+
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
18
|
+
let match = SchemaIRParser.parseJsonSchema(undefined, jsonSchema);
|
|
19
|
+
let ir = match[0];
|
|
20
|
+
let match$1 = IRToTypeGenerator.generateNamedType({
|
|
21
|
+
name: typeName,
|
|
22
|
+
description: jsonSchema.description,
|
|
23
|
+
type_: ir
|
|
24
|
+
}, undefined, undefined, modulePrefix);
|
|
25
|
+
let match$2 = IRToSuryGenerator.generateNamedSchema({
|
|
26
|
+
name: schemaName,
|
|
27
|
+
description: jsonSchema.description,
|
|
28
|
+
type_: ir
|
|
29
|
+
}, undefined, undefined, modulePrefix);
|
|
30
|
+
return [
|
|
31
|
+
match$1[0],
|
|
32
|
+
match$2[0]
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function generateEndpointFunction(endpoint, overrideDir, moduleName) {
|
|
37
|
+
let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
|
|
38
|
+
let requestTypeName = functionName + `Request`;
|
|
39
|
+
let hasRequestBody = Stdlib_Option.isSome(endpoint.requestBody);
|
|
40
|
+
let requestBody = Stdlib_Option.getOr(endpoint.requestBody, {
|
|
41
|
+
description: undefined,
|
|
42
|
+
content: {},
|
|
43
|
+
required: false
|
|
44
|
+
});
|
|
45
|
+
let isRequestBodyRequired = Stdlib_Option.getOr(requestBody.required, false);
|
|
46
|
+
let bodyParam = hasRequestBody ? (
|
|
47
|
+
isRequestBodyRequired ? `~body: ` + requestTypeName : `~body: option<` + requestTypeName + `>=?`
|
|
48
|
+
) : "~body as _";
|
|
49
|
+
let bodyValueConversion = hasRequestBody ? (
|
|
50
|
+
isRequestBodyRequired ? ` let jsonBody = body->S.reverseConvertToJsonOrThrow(` + functionName + `RequestSchema)` : ` let jsonBody = body->Option.map(b => b->S.reverseConvertToJsonOrThrow(` + functionName + `RequestSchema))`
|
|
51
|
+
) : "";
|
|
52
|
+
let successResponse = Stdlib_Array.filterMap([
|
|
53
|
+
"200",
|
|
54
|
+
"201",
|
|
55
|
+
"202",
|
|
56
|
+
"204"
|
|
57
|
+
], code => endpoint.responses[code])[0];
|
|
58
|
+
let responseHandling = Stdlib_Option.mapOr(successResponse, " response", response => Stdlib_Option.mapOr(response.content, " let _ = response\n ()", content => {
|
|
59
|
+
if (Object.entries(content).length !== 0) {
|
|
60
|
+
return ` let value = response->S.parseOrThrow(` + functionName + `ResponseSchema)\n value`;
|
|
61
|
+
} else {
|
|
62
|
+
return " response";
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
let description;
|
|
66
|
+
if (overrideDir !== undefined && moduleName !== undefined) {
|
|
67
|
+
let overrideResult = DocOverride.readOverrideWithValidation(overrideDir, moduleName, functionName, DocOverride.generateEndpointHash(endpoint));
|
|
68
|
+
if (typeof overrideResult !== "object") {
|
|
69
|
+
description = endpoint.description;
|
|
70
|
+
} else {
|
|
71
|
+
switch (overrideResult.TAG) {
|
|
72
|
+
case "ValidOverride" :
|
|
73
|
+
description = overrideResult._0;
|
|
74
|
+
break;
|
|
75
|
+
case "InvalidHash" :
|
|
76
|
+
description = overrideResult.override;
|
|
77
|
+
break;
|
|
78
|
+
case "FileError" :
|
|
79
|
+
description = endpoint.description;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
description = endpoint.description;
|
|
85
|
+
}
|
|
86
|
+
let docComment = CodegenUtils.generateDocString(endpoint.summary, description, undefined);
|
|
87
|
+
let code = `
|
|
88
|
+
|` + docComment.trimEnd() + `
|
|
89
|
+
|let ` + functionName + ` = (` + bodyParam + `, ~fetch: ` + CodegenUtils.fetchTypeSignature + `): promise<` + functionName + `Response> => {
|
|
90
|
+
|` + bodyValueConversion + `
|
|
91
|
+
| fetch(
|
|
92
|
+
| ~url="` + endpoint.path + `",
|
|
93
|
+
| ~method_="` + endpoint.method.toUpperCase() + `",
|
|
94
|
+
| ~body=` + (
|
|
95
|
+
hasRequestBody ? "Some(jsonBody)" : "None"
|
|
96
|
+
) + `,
|
|
97
|
+
| )->Promise.then(response => {
|
|
98
|
+
|` + responseHandling + `
|
|
99
|
+
| ->Promise.resolve
|
|
100
|
+
| })
|
|
101
|
+
|}`;
|
|
102
|
+
return CodegenUtils.trimMargin(code, undefined);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function generateEndpointCode(endpoint, overrideDir, moduleName, modulePrefixOpt) {
|
|
106
|
+
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
107
|
+
let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
|
|
108
|
+
let requestJsonSchema = getJsonSchemaFromRequestBody(endpoint.requestBody);
|
|
109
|
+
let responseJsonSchema = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Array.filterMap([
|
|
110
|
+
"200",
|
|
111
|
+
"201",
|
|
112
|
+
"202",
|
|
113
|
+
"204"
|
|
114
|
+
], code => endpoint.responses[code])[0], resp => resp.content), content => Stdlib_Option.flatMap(Object.entries(content)[0], param => param[1].schema));
|
|
115
|
+
let requestPart = Stdlib_Option.mapOr(requestJsonSchema, "", schema => {
|
|
116
|
+
let match = generateTypeCodeAndSchemaCode(schema, functionName + `Request`, functionName + `Request`, modulePrefix);
|
|
117
|
+
return match[0] + `\n\n` + match[1];
|
|
118
|
+
});
|
|
119
|
+
let responsePart = Stdlib_Option.mapOr(responseJsonSchema, `type ` + functionName + `Response = unit`, schema => {
|
|
120
|
+
let match = generateTypeCodeAndSchemaCode(schema, functionName + `Response`, functionName + `Response`, modulePrefix);
|
|
121
|
+
return match[0] + `\n\n` + match[1];
|
|
122
|
+
});
|
|
123
|
+
return [
|
|
124
|
+
requestPart,
|
|
125
|
+
responsePart,
|
|
126
|
+
generateEndpointFunction(endpoint, overrideDir, moduleName)
|
|
127
|
+
].filter(s => s !== "").join("\n\n");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function generateEndpointModule(endpoint, modulePrefixOpt) {
|
|
131
|
+
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
132
|
+
let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
|
|
133
|
+
let header = CodegenUtils.generateFileHeader(Stdlib_Option.getOr(endpoint.summary, `API: ` + endpoint.path));
|
|
134
|
+
return CodegenUtils.trimMargin(`
|
|
135
|
+
|` + header.trimEnd() + `
|
|
136
|
+
|
|
|
137
|
+
|module ` + Text.toPascalCase(functionName) + ` = {
|
|
138
|
+
|` + CodegenUtils.indent(generateEndpointCode(endpoint, undefined, undefined, modulePrefix), 2) + `
|
|
139
|
+
|}
|
|
140
|
+
|`, undefined);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function generateEndpointsModule(moduleName, endpoints, description, overrideDir, modulePrefixOpt) {
|
|
144
|
+
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
145
|
+
let header = CodegenUtils.generateFileHeader(Stdlib_Option.getOr(description, `API for ` + moduleName));
|
|
146
|
+
let body = endpoints.map(ep => CodegenUtils.indent(generateEndpointCode(ep, overrideDir, moduleName, modulePrefix), 2)).join("\n\n");
|
|
147
|
+
return CodegenUtils.trimMargin(`
|
|
148
|
+
|` + header.trimEnd() + `
|
|
149
|
+
|
|
|
150
|
+
|module ` + moduleName + ` = {
|
|
151
|
+
|` + body + `
|
|
152
|
+
|}
|
|
153
|
+
|`, undefined);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function generateEndpointSignature(endpoint) {
|
|
157
|
+
let functionName = CodegenUtils.generateOperationName(endpoint.operationId, endpoint.path, endpoint.method);
|
|
158
|
+
let summaryPrefix = Stdlib_Option.mapOr(endpoint.summary, "", s => `// ` + s + `\n`);
|
|
159
|
+
let bodyParam = Stdlib_Option.isSome(endpoint.requestBody) ? "~body: 'body, " : "";
|
|
160
|
+
return summaryPrefix + `let ` + functionName + `: (` + bodyParam + `~fetch: fetchFn) => promise<` + functionName + `Response>`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export {
|
|
164
|
+
getJsonSchemaFromRequestBody,
|
|
165
|
+
generateTypeCodeAndSchemaCode,
|
|
166
|
+
generateEndpointFunction,
|
|
167
|
+
generateEndpointCode,
|
|
168
|
+
generateEndpointModule,
|
|
169
|
+
generateEndpointsModule,
|
|
170
|
+
generateEndpointSignature,
|
|
171
|
+
}
|
|
172
|
+
/* @std/text Not a pure module */
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as SchemaIR from "../core/SchemaIR.mjs";
|
|
4
|
+
import * as Text from "@std/text";
|
|
5
|
+
import * as CodegenUtils from "../core/CodegenUtils.mjs";
|
|
6
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
7
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
9
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
10
|
+
import * as GenerationContext from "../types/GenerationContext.mjs";
|
|
11
|
+
import * as ReferenceResolver from "../core/ReferenceResolver.mjs";
|
|
12
|
+
|
|
13
|
+
function applyConstraints(base, min, max, toString) {
|
|
14
|
+
let s1 = min !== undefined ? base + `->S.min(` + toString(Primitive_option.valFromOption(min)) + `)` : base;
|
|
15
|
+
if (max !== undefined) {
|
|
16
|
+
return s1 + `->S.max(` + toString(Primitive_option.valFromOption(max)) + `)`;
|
|
17
|
+
} else {
|
|
18
|
+
return s1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
23
|
+
let depth = depthOpt !== undefined ? depthOpt : 0;
|
|
24
|
+
if (depth > 100) {
|
|
25
|
+
GenerationContext.addWarning(ctx, {
|
|
26
|
+
TAG: "DepthLimitReached",
|
|
27
|
+
depth: depth,
|
|
28
|
+
path: ctx.path
|
|
29
|
+
});
|
|
30
|
+
return "S.json";
|
|
31
|
+
}
|
|
32
|
+
let recurse = nextIrType => generateSchemaWithContext(ctx, depth + 1 | 0, nextIrType);
|
|
33
|
+
if (typeof irType !== "object") {
|
|
34
|
+
switch (irType) {
|
|
35
|
+
case "Boolean" :
|
|
36
|
+
return "S.bool";
|
|
37
|
+
case "Null" :
|
|
38
|
+
return "S.null";
|
|
39
|
+
case "Unknown" :
|
|
40
|
+
return "S.json";
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
switch (irType.TAG) {
|
|
44
|
+
case "String" :
|
|
45
|
+
let c = irType.constraints;
|
|
46
|
+
let s = applyConstraints("S.string", c.minLength, c.maxLength, v => v.toString());
|
|
47
|
+
let p = c.pattern;
|
|
48
|
+
if (p !== undefined) {
|
|
49
|
+
return s + `->S.pattern(%re("/` + CodegenUtils.escapeString(p) + `/"))`;
|
|
50
|
+
} else {
|
|
51
|
+
return s;
|
|
52
|
+
}
|
|
53
|
+
case "Number" :
|
|
54
|
+
let c$1 = irType.constraints;
|
|
55
|
+
return applyConstraints("S.float", c$1.minimum, c$1.maximum, v => (v | 0).toString());
|
|
56
|
+
case "Integer" :
|
|
57
|
+
let c$2 = irType.constraints;
|
|
58
|
+
return applyConstraints("S.int", c$2.minimum, c$2.maximum, v => (v | 0).toString());
|
|
59
|
+
case "Array" :
|
|
60
|
+
let c$3 = irType.constraints;
|
|
61
|
+
return applyConstraints(`S.array(` + recurse(irType.items) + `)`, c$3.minItems, c$3.maxItems, v => v.toString());
|
|
62
|
+
case "Object" :
|
|
63
|
+
let additionalProperties = irType.additionalProperties;
|
|
64
|
+
let properties = irType.properties;
|
|
65
|
+
if (properties.length === 0) {
|
|
66
|
+
if (additionalProperties !== undefined) {
|
|
67
|
+
return `S.dict(` + recurse(additionalProperties) + `)`;
|
|
68
|
+
} else {
|
|
69
|
+
return "S.json";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let fields = properties.map(param => {
|
|
73
|
+
let name = param[0];
|
|
74
|
+
let schemaCode = recurse(param[1]);
|
|
75
|
+
let camelName = CodegenUtils.escapeKeyword(Text.toCamelCase(name));
|
|
76
|
+
if (param[2]) {
|
|
77
|
+
return ` ` + camelName + `: s.field("` + name + `", ` + schemaCode + `),`;
|
|
78
|
+
} else {
|
|
79
|
+
return ` ` + camelName + `: s.fieldOr("` + name + `", S.nullableAsOption(` + schemaCode + `), None),`;
|
|
80
|
+
}
|
|
81
|
+
}).join("\n");
|
|
82
|
+
return `S.object(s => {\n` + fields + `\n })`;
|
|
83
|
+
case "Literal" :
|
|
84
|
+
let value = irType._0;
|
|
85
|
+
if (typeof value !== "object") {
|
|
86
|
+
return "S.literal(null)";
|
|
87
|
+
}
|
|
88
|
+
switch (value.TAG) {
|
|
89
|
+
case "StringLiteral" :
|
|
90
|
+
return `S.literal("` + CodegenUtils.escapeString(value._0) + `")`;
|
|
91
|
+
case "NumberLiteral" :
|
|
92
|
+
return `S.literal(` + value._0.toString() + `)`;
|
|
93
|
+
case "BooleanLiteral" :
|
|
94
|
+
return `S.literal(` + (
|
|
95
|
+
value._0 ? "true" : "false"
|
|
96
|
+
) + `)`;
|
|
97
|
+
}
|
|
98
|
+
case "Union" :
|
|
99
|
+
let types = irType._0;
|
|
100
|
+
let match = Stdlib_Array.reduce(types, [
|
|
101
|
+
false,
|
|
102
|
+
false,
|
|
103
|
+
undefined,
|
|
104
|
+
undefined
|
|
105
|
+
], (param, t) => {
|
|
106
|
+
let arrItem = param[2];
|
|
107
|
+
let hArr = param[0];
|
|
108
|
+
if (typeof t !== "object" || t.TAG !== "Array") {
|
|
109
|
+
return [
|
|
110
|
+
hArr,
|
|
111
|
+
true,
|
|
112
|
+
arrItem,
|
|
113
|
+
t
|
|
114
|
+
];
|
|
115
|
+
} else {
|
|
116
|
+
return [
|
|
117
|
+
true,
|
|
118
|
+
param[1],
|
|
119
|
+
t.items,
|
|
120
|
+
param[3]
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
let arrayItemType = match[2];
|
|
125
|
+
if (match[0] && match[1] && SchemaIR.equals(Stdlib_Option.getOr(arrayItemType, "Unknown"), Stdlib_Option.getOr(match[3], "Unknown"))) {
|
|
126
|
+
return `S.array(` + recurse(Stdlib_Option.getOr(arrayItemType, "Unknown")) + `)`;
|
|
127
|
+
} else if (types.every(t => {
|
|
128
|
+
if (typeof t !== "object") {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (t.TAG !== "Literal") {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
let tmp = t._0;
|
|
135
|
+
return typeof tmp !== "object" ? false : tmp.TAG === "StringLiteral";
|
|
136
|
+
}) && types.length !== 0 && types.length <= 50) {
|
|
137
|
+
return `S.union([` + types.map(recurse).join(", ") + `])`;
|
|
138
|
+
} else {
|
|
139
|
+
GenerationContext.addWarning(ctx, {
|
|
140
|
+
TAG: "ComplexUnionSimplified",
|
|
141
|
+
location: ctx.path,
|
|
142
|
+
types: types.map(SchemaIR.toString).join(" | ")
|
|
143
|
+
});
|
|
144
|
+
return "S.json";
|
|
145
|
+
}
|
|
146
|
+
case "Intersection" :
|
|
147
|
+
let types$1 = irType._0;
|
|
148
|
+
if (types$1.every(t => typeof t !== "object" ? false : t.TAG === "Reference") && types$1.length !== 0) {
|
|
149
|
+
return recurse(Stdlib_Option.getOr(types$1[types$1.length - 1 | 0], "Unknown"));
|
|
150
|
+
} else {
|
|
151
|
+
GenerationContext.addWarning(ctx, {
|
|
152
|
+
TAG: "IntersectionNotFullySupported",
|
|
153
|
+
location: ctx.path,
|
|
154
|
+
note: "Complex intersection"
|
|
155
|
+
});
|
|
156
|
+
return "S.json";
|
|
157
|
+
}
|
|
158
|
+
case "Reference" :
|
|
159
|
+
let ref = irType._0;
|
|
160
|
+
let available = ctx.availableSchemas;
|
|
161
|
+
let schemaPath;
|
|
162
|
+
if (available !== undefined) {
|
|
163
|
+
let name = Stdlib_Option.getOr(ref.split("/")[ref.split("/").length - 1 | 0], "");
|
|
164
|
+
schemaPath = available.includes(name) ? Text.toPascalCase(name) + `.schema` : `ComponentSchemas.` + Text.toPascalCase(name) + `.schema`;
|
|
165
|
+
} else {
|
|
166
|
+
schemaPath = Stdlib_Option.getOr(ReferenceResolver.refToSchemaPath(ctx.insideComponentSchemas, ctx.modulePrefix, ref), "S.json");
|
|
167
|
+
}
|
|
168
|
+
if (schemaPath === "S.json") {
|
|
169
|
+
GenerationContext.addWarning(ctx, {
|
|
170
|
+
TAG: "FallbackToJson",
|
|
171
|
+
reason: `Unresolved ref: ` + ref,
|
|
172
|
+
context: {
|
|
173
|
+
path: ctx.path,
|
|
174
|
+
operation: "gen ref",
|
|
175
|
+
schema: undefined
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return schemaPath;
|
|
180
|
+
case "Option" :
|
|
181
|
+
return `S.nullableAsOption(` + recurse(irType._0) + `)`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function generateSchema(depthOpt, pathOpt, insideComponentSchemasOpt, availableSchemas, modulePrefixOpt, irType) {
|
|
187
|
+
let depth = depthOpt !== undefined ? depthOpt : 0;
|
|
188
|
+
let path = pathOpt !== undefined ? pathOpt : "";
|
|
189
|
+
let insideComponentSchemas = insideComponentSchemasOpt !== undefined ? insideComponentSchemasOpt : false;
|
|
190
|
+
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
191
|
+
let ctx = GenerationContext.make(path, insideComponentSchemas, availableSchemas, modulePrefix, undefined);
|
|
192
|
+
return [
|
|
193
|
+
generateSchemaWithContext(ctx, depth, irType),
|
|
194
|
+
ctx.warnings
|
|
195
|
+
];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function generateNamedSchema(namedSchema, insideComponentSchemasOpt, availableSchemas, modulePrefixOpt) {
|
|
199
|
+
let insideComponentSchemas = insideComponentSchemasOpt !== undefined ? insideComponentSchemasOpt : false;
|
|
200
|
+
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
201
|
+
let ctx = GenerationContext.make(`schema.` + namedSchema.name, insideComponentSchemas, availableSchemas, modulePrefix, undefined);
|
|
202
|
+
let d = namedSchema.description;
|
|
203
|
+
let doc = d !== undefined ? CodegenUtils.generateDocComment(undefined, d, undefined) : "";
|
|
204
|
+
return [
|
|
205
|
+
doc + `let ` + namedSchema.name + `Schema = ` + generateSchemaWithContext(ctx, 0, namedSchema.type_),
|
|
206
|
+
ctx.warnings
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function generateAllSchemas(context) {
|
|
211
|
+
let warnings = [];
|
|
212
|
+
let schemas = Object.values(context.schemas).toSorted((a, b) => Primitive_string.compare(a.name, b.name)).map(s => {
|
|
213
|
+
let match = generateNamedSchema(s, undefined, undefined, undefined);
|
|
214
|
+
warnings.push(...match[1]);
|
|
215
|
+
return match[0];
|
|
216
|
+
});
|
|
217
|
+
return [
|
|
218
|
+
schemas,
|
|
219
|
+
warnings
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let addWarning = GenerationContext.addWarning;
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
addWarning,
|
|
227
|
+
applyConstraints,
|
|
228
|
+
generateSchemaWithContext,
|
|
229
|
+
generateSchema,
|
|
230
|
+
generateNamedSchema,
|
|
231
|
+
generateAllSchemas,
|
|
232
|
+
}
|
|
233
|
+
/* @std/text Not a pure module */
|