@f3liz/rescript-autogen-openapi 0.2.0 → 0.3.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.
Potentially problematic release.
This version of @f3liz/rescript-autogen-openapi might be problematic. Click here for more details.
- package/lib/es6/src/bindings/Toposort.mjs +12 -0
- package/lib/es6/src/core/CodegenUtils.mjs +75 -0
- package/lib/es6/src/core/SchemaIR.mjs +72 -2
- package/lib/es6/src/core/SchemaIRParser.mjs +244 -51
- package/lib/es6/src/generators/ComponentSchemaGenerator.mjs +118 -36
- package/lib/es6/src/generators/EndpointGenerator.mjs +4 -3
- package/lib/es6/src/generators/IRToSuryGenerator.mjs +271 -34
- package/lib/es6/src/generators/IRToTypeGenerator.mjs +491 -285
- package/lib/es6/src/generators/IRToTypeScriptGenerator.mjs +1 -1
- package/lib/es6/src/generators/ModuleGenerator.mjs +1 -1
- package/lib/es6/src/generators/SchemaCodeGenerator.mjs +1 -1
- package/lib/es6/src/types/GenerationContext.mjs +25 -2
- package/package.json +3 -2
- package/src/bindings/Toposort.res +16 -0
- package/src/core/CodegenUtils.res +50 -0
- package/src/core/SchemaIR.res +33 -0
- package/src/core/SchemaIRParser.res +96 -2
- package/src/generators/ComponentSchemaGenerator.res +133 -50
- package/src/generators/EndpointGenerator.res +7 -3
- package/src/generators/IRToSuryGenerator.res +175 -40
- package/src/generators/IRToTypeGenerator.res +212 -63
- package/src/generators/IRToTypeScriptGenerator.res +6 -1
- package/src/generators/ModuleGenerator.res +2 -1
- package/src/generators/SchemaCodeGenerator.res +2 -1
- package/src/types/GenerationContext.res +34 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Pipeline from "../core/Pipeline.mjs";
|
|
4
|
+
import * as Toposort from "../bindings/Toposort.mjs";
|
|
4
5
|
import * as FileSystem from "../core/FileSystem.mjs";
|
|
5
6
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
6
7
|
import * as CodegenUtils from "../core/CodegenUtils.mjs";
|
|
@@ -29,8 +30,7 @@ function extractReferencedSchemaNames(_irType) {
|
|
|
29
30
|
case "Intersection" :
|
|
30
31
|
return irType._0.flatMap(extractReferencedSchemaNames);
|
|
31
32
|
case "Reference" :
|
|
32
|
-
|
|
33
|
-
return [Stdlib_Option.getOr(parts[parts.length - 1 | 0], "")];
|
|
33
|
+
return [irType._0];
|
|
34
34
|
case "Option" :
|
|
35
35
|
_irType = irType._0;
|
|
36
36
|
continue;
|
|
@@ -56,7 +56,8 @@ function generate(spec, outputDir) {
|
|
|
56
56
|
s.name,
|
|
57
57
|
s
|
|
58
58
|
]));
|
|
59
|
-
let
|
|
59
|
+
let allNodes = schemas.map(s => s.name);
|
|
60
|
+
let edges = schemas.flatMap(schema => {
|
|
60
61
|
let references = extractReferencedSchemaNames(schema.type_).filter(name => {
|
|
61
62
|
if (name in schemaNameMap) {
|
|
62
63
|
return name !== schema.name;
|
|
@@ -64,50 +65,131 @@ function generate(spec, outputDir) {
|
|
|
64
65
|
return false;
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
return references.map(dep => [
|
|
69
|
+
schema.name,
|
|
70
|
+
dep
|
|
71
|
+
]);
|
|
69
72
|
});
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let schema = v !== undefined ? v : schemas[0];
|
|
80
|
-
sortedSchemas.push(schema);
|
|
81
|
-
schemas.forEach(otherSchema => {
|
|
82
|
-
let dependsOnCurrent = Stdlib_Option.getOr(dependencyMap[otherSchema.name], []).some(name => name === schema.name);
|
|
83
|
-
if (!dependsOnCurrent) {
|
|
73
|
+
let sortedNames;
|
|
74
|
+
try {
|
|
75
|
+
sortedNames = Toposort.sortArray(allNodes, edges).toReversed();
|
|
76
|
+
} catch (exn) {
|
|
77
|
+
let visited = {};
|
|
78
|
+
let inStack = {};
|
|
79
|
+
let cycleEdges = [];
|
|
80
|
+
let dfs = node => {
|
|
81
|
+
if (Stdlib_Option.getOr(inStack[node], false) || Stdlib_Option.getOr(visited[node], false)) {
|
|
84
82
|
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
} else {
|
|
84
|
+
visited[node] = true;
|
|
85
|
+
inStack[node] = true;
|
|
86
|
+
edges.forEach(param => {
|
|
87
|
+
let from = param[0];
|
|
88
|
+
if (from !== node) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let to = param[1];
|
|
92
|
+
if (Stdlib_Option.getOr(inStack[to], false)) {
|
|
93
|
+
cycleEdges.push([
|
|
94
|
+
from,
|
|
95
|
+
to
|
|
96
|
+
]);
|
|
97
|
+
return;
|
|
98
|
+
} else {
|
|
99
|
+
return dfs(to);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
inStack[node] = false;
|
|
91
103
|
return;
|
|
92
104
|
}
|
|
105
|
+
};
|
|
106
|
+
allNodes.forEach(dfs);
|
|
107
|
+
let nonCycleEdges = edges.filter(param => {
|
|
108
|
+
let to = param[1];
|
|
109
|
+
let from = param[0];
|
|
110
|
+
return !cycleEdges.some(param => {
|
|
111
|
+
if (param[0] === from) {
|
|
112
|
+
return param[1] === to;
|
|
113
|
+
} else {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
93
117
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
118
|
+
try {
|
|
119
|
+
sortedNames = Toposort.sortArray(allNodes, nonCycleEdges).toReversed();
|
|
120
|
+
} catch (exn$1) {
|
|
121
|
+
sortedNames = allNodes.toSorted(Primitive_string.compare);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
let finalSortedSchemas = Stdlib_Array.filterMap(sortedNames, name => schemaNameMap[name]);
|
|
98
125
|
let availableSchemaNames = finalSortedSchemas.map(s => s.name);
|
|
99
126
|
let warnings = match[1].slice();
|
|
127
|
+
let selfRefSchemas = {};
|
|
128
|
+
finalSortedSchemas.forEach(schema => {
|
|
129
|
+
let refs = extractReferencedSchemaNames(schema.type_);
|
|
130
|
+
if (refs.some(name => name === schema.name)) {
|
|
131
|
+
selfRefSchemas[schema.name] = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
100
135
|
let moduleCodes = finalSortedSchemas.map(schema => {
|
|
101
|
-
let
|
|
102
|
-
let
|
|
103
|
-
let
|
|
104
|
-
let
|
|
136
|
+
let isSelfRef = Stdlib_Option.getOr(selfRefSchemas[schema.name], false);
|
|
137
|
+
let selfRefName = isSelfRef ? schema.name : undefined;
|
|
138
|
+
let typeCtx = GenerationContext.make(`ComponentSchemas.` + schema.name, true, availableSchemaNames, undefined, selfRefName, undefined);
|
|
139
|
+
let typeCode = IRToTypeGenerator.generateTypeWithContext(typeCtx, 0, undefined, schema.type_);
|
|
140
|
+
let processed = 0;
|
|
141
|
+
while (processed < typeCtx.extractedTypes.length) {
|
|
142
|
+
let idx = processed;
|
|
143
|
+
let match = typeCtx.extractedTypes[idx];
|
|
144
|
+
let irType = match.irType;
|
|
145
|
+
if (match.isUnboxed && typeof irType === "object" && irType.TAG === "Union") {
|
|
146
|
+
irType._0.forEach(memberType => {
|
|
147
|
+
IRToTypeGenerator.generateTypeWithContext(typeCtx, 0, true, memberType);
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
IRToTypeGenerator.generateTypeWithContext(typeCtx, 0, false, irType);
|
|
151
|
+
}
|
|
152
|
+
processed = idx + 1 | 0;
|
|
153
|
+
};
|
|
154
|
+
let allExtracted = typeCtx.extractedTypes.slice().toReversed();
|
|
155
|
+
let extractedTypeMap = allExtracted.length !== 0 ? allExtracted : undefined;
|
|
156
|
+
let schemaCtx = GenerationContext.make(`ComponentSchemas.` + schema.name, true, availableSchemaNames, undefined, selfRefName, undefined);
|
|
157
|
+
let schemaCode = IRToSuryGenerator.generateSchemaWithContext(schemaCtx, 0, extractedTypeMap, schema.type_);
|
|
105
158
|
warnings.push(...typeCtx.warnings);
|
|
106
159
|
warnings.push(...schemaCtx.warnings);
|
|
160
|
+
let extractedTypeDefs = allExtracted.map(param => {
|
|
161
|
+
let irType = param.irType;
|
|
162
|
+
let typeName = param.typeName;
|
|
163
|
+
let auxTypeCode;
|
|
164
|
+
if (param.isUnboxed) {
|
|
165
|
+
let exit = 0;
|
|
166
|
+
if (typeof irType !== "object" || irType.TAG !== "Union") {
|
|
167
|
+
exit = 1;
|
|
168
|
+
} else {
|
|
169
|
+
let body = IRToTypeGenerator.generateUnboxedVariantBody(typeCtx, irType._0);
|
|
170
|
+
auxTypeCode = `@unboxed type ` + typeName + ` = ` + body;
|
|
171
|
+
}
|
|
172
|
+
if (exit === 1) {
|
|
173
|
+
let auxType = IRToTypeGenerator.generateTypeWithContext(typeCtx, 0, undefined, irType);
|
|
174
|
+
auxTypeCode = `type ` + typeName + ` = ` + auxType;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
let auxType$1 = IRToTypeGenerator.generateTypeWithContext(typeCtx, 0, undefined, irType);
|
|
178
|
+
auxTypeCode = `type ` + typeName + ` = ` + auxType$1;
|
|
179
|
+
}
|
|
180
|
+
let auxSchemaCtx = GenerationContext.make(`ComponentSchemas.` + schema.name + `.` + typeName, true, availableSchemaNames, undefined, undefined, undefined);
|
|
181
|
+
let filteredMap = allExtracted.filter(param => param.typeName !== typeName);
|
|
182
|
+
let auxExtractedTypeMap = filteredMap.length !== 0 ? filteredMap : undefined;
|
|
183
|
+
let auxSchema = IRToSuryGenerator.generateSchemaWithContext(auxSchemaCtx, 0, auxExtractedTypeMap, irType);
|
|
184
|
+
return ` ` + auxTypeCode + `\n let ` + typeName + `Schema = ` + auxSchema;
|
|
185
|
+
});
|
|
107
186
|
let docComment = Stdlib_Option.mapOr(schema.description, "", d => CodegenUtils.generateDocString(undefined, d, undefined));
|
|
187
|
+
let extractedBlock = extractedTypeDefs.length !== 0 ? extractedTypeDefs.join("\n") + "\n" : "";
|
|
188
|
+
let typeKeyword = isSelfRef ? "type rec t" : "type t";
|
|
189
|
+
let finalSchemaCode = isSelfRef ? `S.recursive("` + schema.name + `", schema => ` + schemaCode + `)` : schemaCode;
|
|
108
190
|
return docComment + `module ` + JsConvertCase.toPascalCase(schema.name) + ` = {
|
|
109
|
-
|
|
110
|
-
let schema = ` +
|
|
191
|
+
` + extractedBlock + ` ` + typeKeyword + ` = ` + typeCode + `
|
|
192
|
+
let schema = ` + finalSchemaCode + `
|
|
111
193
|
}`;
|
|
112
194
|
});
|
|
113
195
|
let fileHeader = CodegenUtils.generateFileHeader("Shared component schemas");
|
|
@@ -122,4 +204,4 @@ export {
|
|
|
122
204
|
extractReferencedSchemaNames,
|
|
123
205
|
generate,
|
|
124
206
|
}
|
|
125
|
-
/*
|
|
207
|
+
/* Toposort Not a pure module */
|
|
@@ -26,7 +26,7 @@ function generateTypeCodeAndSchemaCode(jsonSchema, typeName, schemaName, moduleP
|
|
|
26
26
|
name: schemaName,
|
|
27
27
|
description: jsonSchema.description,
|
|
28
28
|
type_: ir
|
|
29
|
-
}, undefined, undefined, modulePrefix);
|
|
29
|
+
}, undefined, undefined, modulePrefix, match$1[2]);
|
|
30
30
|
return [
|
|
31
31
|
match$1[0],
|
|
32
32
|
match$2[0]
|
|
@@ -45,7 +45,8 @@ function generateEndpointFunction(endpoint, overrideDir, moduleName) {
|
|
|
45
45
|
let isRequestBodyRequired = Stdlib_Option.getOr(requestBody.required, false);
|
|
46
46
|
let bodyParam = hasRequestBody ? (
|
|
47
47
|
isRequestBodyRequired ? `~body: ` + requestTypeName : `~body: option<` + requestTypeName + `>=?`
|
|
48
|
-
) : "
|
|
48
|
+
) : "";
|
|
49
|
+
let paramSep = hasRequestBody ? ", " : "";
|
|
49
50
|
let bodyValueConversion = hasRequestBody ? (
|
|
50
51
|
isRequestBodyRequired ? ` let jsonBody = body->S.reverseConvertToJsonOrThrow(` + functionName + `RequestSchema)` : ` let jsonBody = body->Option.map(b => b->S.reverseConvertToJsonOrThrow(` + functionName + `RequestSchema))`
|
|
51
52
|
) : "";
|
|
@@ -86,7 +87,7 @@ function generateEndpointFunction(endpoint, overrideDir, moduleName) {
|
|
|
86
87
|
let docComment = CodegenUtils.generateDocString(endpoint.summary, description, undefined);
|
|
87
88
|
let code = `
|
|
88
89
|
|` + docComment.trimEnd() + `
|
|
89
|
-
|let ` + functionName + ` = (` + bodyParam +
|
|
90
|
+
|let ` + functionName + ` = (` + bodyParam + paramSep + `~fetch: ` + CodegenUtils.fetchTypeSignature + `): promise<` + functionName + `Response> => {
|
|
90
91
|
|` + bodyValueConversion + `
|
|
91
92
|
| fetch(
|
|
92
93
|
| ~url="` + endpoint.path + `",
|
|
@@ -19,7 +19,7 @@ function applyConstraints(base, min, max, toString) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
22
|
+
function generateSchemaWithContext(ctx, depthOpt, extractedTypeMap, irType) {
|
|
23
23
|
let depth = depthOpt !== undefined ? depthOpt : 0;
|
|
24
24
|
if (depth > 100) {
|
|
25
25
|
GenerationContext.addWarning(ctx, {
|
|
@@ -29,7 +29,11 @@ function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
|
29
29
|
});
|
|
30
30
|
return "S.json";
|
|
31
31
|
}
|
|
32
|
-
let recurse = nextIrType => generateSchemaWithContext(ctx, depth + 1 | 0, nextIrType);
|
|
32
|
+
let recurse = nextIrType => generateSchemaWithContext(ctx, depth + 1 | 0, extractedTypeMap, nextIrType);
|
|
33
|
+
let foundExtracted = extractedTypeMap !== undefined ? extractedTypeMap.find(param => SchemaIR.equals(param.irType, irType)) : undefined;
|
|
34
|
+
if (foundExtracted !== undefined) {
|
|
35
|
+
return foundExtracted.typeName + `Schema`;
|
|
36
|
+
}
|
|
33
37
|
if (typeof irType !== "object") {
|
|
34
38
|
switch (irType) {
|
|
35
39
|
case "Boolean" :
|
|
@@ -66,15 +70,46 @@ function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
|
66
70
|
if (additionalProperties !== undefined) {
|
|
67
71
|
return `S.dict(` + recurse(additionalProperties) + `)`;
|
|
68
72
|
} else {
|
|
69
|
-
return "S.json";
|
|
73
|
+
return "S.dict(S.json)";
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
let fields = properties.map(param => {
|
|
77
|
+
let fieldType = param[1];
|
|
73
78
|
let name = param[0];
|
|
74
|
-
let schemaCode = recurse(
|
|
79
|
+
let schemaCode = recurse(fieldType);
|
|
75
80
|
let camelName = CodegenUtils.escapeKeyword(JsConvertCase.toCamelCase(name));
|
|
81
|
+
let alreadyNullable = true;
|
|
82
|
+
if (!schemaCode.startsWith("S.nullableAsOption(")) {
|
|
83
|
+
let tmp;
|
|
84
|
+
if (typeof fieldType !== "object") {
|
|
85
|
+
tmp = false;
|
|
86
|
+
} else {
|
|
87
|
+
switch (fieldType.TAG) {
|
|
88
|
+
case "Union" :
|
|
89
|
+
tmp = fieldType._0.some(t => {
|
|
90
|
+
if (typeof t !== "object") {
|
|
91
|
+
return t === "Null";
|
|
92
|
+
}
|
|
93
|
+
if (t.TAG !== "Literal") {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
let tmp = t._0;
|
|
97
|
+
return typeof tmp !== "object";
|
|
98
|
+
});
|
|
99
|
+
break;
|
|
100
|
+
case "Option" :
|
|
101
|
+
tmp = true;
|
|
102
|
+
break;
|
|
103
|
+
default:
|
|
104
|
+
tmp = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
alreadyNullable = tmp;
|
|
108
|
+
}
|
|
76
109
|
if (param[2]) {
|
|
77
110
|
return ` ` + camelName + `: s.field("` + name + `", ` + schemaCode + `),`;
|
|
111
|
+
} else if (alreadyNullable) {
|
|
112
|
+
return ` ` + camelName + `: s.fieldOr("` + name + `", ` + schemaCode + `, None),`;
|
|
78
113
|
} else {
|
|
79
114
|
return ` ` + camelName + `: s.fieldOr("` + name + `", S.nullableAsOption(` + schemaCode + `), None),`;
|
|
80
115
|
}
|
|
@@ -137,24 +172,150 @@ function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
|
137
172
|
}
|
|
138
173
|
});
|
|
139
174
|
let arrayItemType = match[2];
|
|
140
|
-
let result
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
175
|
+
let result;
|
|
176
|
+
if (match[0] && match[1] && effectiveTypes.length === 2 && SchemaIR.equals(Stdlib_Option.getOr(arrayItemType, "Unknown"), Stdlib_Option.getOr(match[3], "Unknown"))) {
|
|
177
|
+
result = `S.array(` + recurse(Stdlib_Option.getOr(arrayItemType, "Unknown")) + `)`;
|
|
178
|
+
} else if (effectiveTypes.every(t => {
|
|
179
|
+
if (typeof t !== "object") {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (t.TAG !== "Literal") {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
let tmp = t._0;
|
|
186
|
+
if (typeof tmp !== "object") {
|
|
187
|
+
return false;
|
|
188
|
+
} else {
|
|
189
|
+
return tmp.TAG === "StringLiteral";
|
|
190
|
+
}
|
|
191
|
+
}) && effectiveTypes.length !== 0 && effectiveTypes.length <= 50) {
|
|
192
|
+
result = `S.union([` + effectiveTypes.map(recurse).join(", ") + `])`;
|
|
193
|
+
} else if (effectiveTypes.length !== 0) {
|
|
194
|
+
let runtimeKinds = {};
|
|
195
|
+
effectiveTypes.forEach(t => {
|
|
196
|
+
let kind;
|
|
197
|
+
if (typeof t !== "object") {
|
|
198
|
+
switch (t) {
|
|
199
|
+
case "Boolean" :
|
|
200
|
+
kind = "boolean";
|
|
201
|
+
break;
|
|
202
|
+
case "Null" :
|
|
203
|
+
kind = "null";
|
|
204
|
+
break;
|
|
205
|
+
default:
|
|
206
|
+
kind = "unknown";
|
|
144
207
|
}
|
|
145
|
-
|
|
146
|
-
|
|
208
|
+
} else {
|
|
209
|
+
switch (t.TAG) {
|
|
210
|
+
case "String" :
|
|
211
|
+
kind = "string";
|
|
212
|
+
break;
|
|
213
|
+
case "Number" :
|
|
214
|
+
case "Integer" :
|
|
215
|
+
kind = "number";
|
|
216
|
+
break;
|
|
217
|
+
case "Array" :
|
|
218
|
+
kind = "array";
|
|
219
|
+
break;
|
|
220
|
+
case "Literal" :
|
|
221
|
+
let tmp = t._0;
|
|
222
|
+
if (typeof tmp !== "object") {
|
|
223
|
+
kind = "null";
|
|
224
|
+
} else {
|
|
225
|
+
switch (tmp.TAG) {
|
|
226
|
+
case "StringLiteral" :
|
|
227
|
+
kind = "string";
|
|
228
|
+
break;
|
|
229
|
+
case "NumberLiteral" :
|
|
230
|
+
kind = "number";
|
|
231
|
+
break;
|
|
232
|
+
case "BooleanLiteral" :
|
|
233
|
+
kind = "boolean";
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case "Object" :
|
|
239
|
+
case "Intersection" :
|
|
240
|
+
case "Reference" :
|
|
241
|
+
kind = "object";
|
|
242
|
+
break;
|
|
243
|
+
default:
|
|
244
|
+
kind = "unknown";
|
|
147
245
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
246
|
+
}
|
|
247
|
+
let count = Stdlib_Option.getOr(runtimeKinds[kind], 0);
|
|
248
|
+
runtimeKinds[kind] = count + 1 | 0;
|
|
249
|
+
});
|
|
250
|
+
let canUnbox = Object.values(runtimeKinds).every(count => count <= 1);
|
|
251
|
+
if (canUnbox) {
|
|
252
|
+
let rawNames = effectiveTypes.map(CodegenUtils.variantConstructorName);
|
|
253
|
+
let names = CodegenUtils.deduplicateNames(rawNames);
|
|
254
|
+
let branches = effectiveTypes.map((memberType, i) => {
|
|
255
|
+
let constructorName = names[i];
|
|
256
|
+
if (typeof memberType === "object" && memberType.TAG === "Object") {
|
|
257
|
+
let additionalProperties = memberType.additionalProperties;
|
|
258
|
+
let properties = memberType.properties;
|
|
259
|
+
if (properties.length === 0) {
|
|
260
|
+
if (additionalProperties !== undefined) {
|
|
261
|
+
return `S.dict(` + recurse(additionalProperties) + `)->S.shape(v => ` + constructorName + `(v))`;
|
|
262
|
+
} else {
|
|
263
|
+
return `S.dict(S.json)->S.shape(v => ` + constructorName + `(v))`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
let fields = properties.map(param => {
|
|
267
|
+
let fieldType = param[1];
|
|
268
|
+
let name = param[0];
|
|
269
|
+
let schemaCode = recurse(fieldType);
|
|
270
|
+
let camelName = CodegenUtils.escapeKeyword(JsConvertCase.toCamelCase(name));
|
|
271
|
+
let alreadyNullable = true;
|
|
272
|
+
if (!schemaCode.startsWith("S.nullableAsOption(")) {
|
|
273
|
+
let tmp;
|
|
274
|
+
if (typeof fieldType !== "object") {
|
|
275
|
+
tmp = false;
|
|
276
|
+
} else {
|
|
277
|
+
switch (fieldType.TAG) {
|
|
278
|
+
case "Union" :
|
|
279
|
+
tmp = fieldType._0.some(t => {
|
|
280
|
+
if (typeof t !== "object") {
|
|
281
|
+
return t === "Null";
|
|
282
|
+
}
|
|
283
|
+
if (t.TAG !== "Literal") {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
let tmp = t._0;
|
|
287
|
+
return typeof tmp !== "object";
|
|
288
|
+
});
|
|
289
|
+
break;
|
|
290
|
+
case "Option" :
|
|
291
|
+
tmp = true;
|
|
292
|
+
break;
|
|
293
|
+
default:
|
|
294
|
+
tmp = false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
alreadyNullable = tmp;
|
|
298
|
+
}
|
|
299
|
+
if (param[2]) {
|
|
300
|
+
return ` ` + camelName + `: s.field("` + name + `", ` + schemaCode + `),`;
|
|
301
|
+
} else if (alreadyNullable) {
|
|
302
|
+
return ` ` + camelName + `: s.fieldOr("` + name + `", ` + schemaCode + `, None),`;
|
|
303
|
+
} else {
|
|
304
|
+
return ` ` + camelName + `: s.fieldOr("` + name + `", S.nullableAsOption(` + schemaCode + `), None),`;
|
|
305
|
+
}
|
|
306
|
+
}).join("\n");
|
|
307
|
+
return `S.object(s => ` + constructorName + `({\n` + fields + `\n }))`;
|
|
153
308
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
309
|
+
let innerSchema = recurse(memberType);
|
|
310
|
+
return innerSchema + `->S.shape(v => ` + constructorName + `(v))`;
|
|
311
|
+
});
|
|
312
|
+
result = `S.union([` + branches.join(", ") + `])`;
|
|
313
|
+
} else {
|
|
314
|
+
result = recurse(effectiveTypes[effectiveTypes.length - 1 | 0]);
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
result = "S.json";
|
|
318
|
+
}
|
|
158
319
|
if (hasNull) {
|
|
159
320
|
return `S.nullableAsOption(` + result + `)`;
|
|
160
321
|
} else {
|
|
@@ -198,11 +359,42 @@ function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
|
198
359
|
let objectProps = match$1[0];
|
|
199
360
|
if (objectProps.length !== 0 && nonObjectTypes.length === 0) {
|
|
200
361
|
let fields$1 = objectProps.map(param => {
|
|
362
|
+
let fieldType = param[1];
|
|
201
363
|
let name = param[0];
|
|
202
|
-
let schemaCode = recurse(
|
|
364
|
+
let schemaCode = recurse(fieldType);
|
|
203
365
|
let camelName = CodegenUtils.escapeKeyword(JsConvertCase.toCamelCase(name));
|
|
366
|
+
let alreadyNullable = true;
|
|
367
|
+
if (!schemaCode.startsWith("S.nullableAsOption(")) {
|
|
368
|
+
let tmp;
|
|
369
|
+
if (typeof fieldType !== "object") {
|
|
370
|
+
tmp = false;
|
|
371
|
+
} else {
|
|
372
|
+
switch (fieldType.TAG) {
|
|
373
|
+
case "Union" :
|
|
374
|
+
tmp = fieldType._0.some(t => {
|
|
375
|
+
if (typeof t !== "object") {
|
|
376
|
+
return t === "Null";
|
|
377
|
+
}
|
|
378
|
+
if (t.TAG !== "Literal") {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
let tmp = t._0;
|
|
382
|
+
return typeof tmp !== "object";
|
|
383
|
+
});
|
|
384
|
+
break;
|
|
385
|
+
case "Option" :
|
|
386
|
+
tmp = true;
|
|
387
|
+
break;
|
|
388
|
+
default:
|
|
389
|
+
tmp = false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
alreadyNullable = tmp;
|
|
393
|
+
}
|
|
204
394
|
if (param[2]) {
|
|
205
395
|
return ` ` + camelName + `: s.field("` + name + `", ` + schemaCode + `),`;
|
|
396
|
+
} else if (alreadyNullable) {
|
|
397
|
+
return ` ` + camelName + `: s.fieldOr("` + name + `", ` + schemaCode + `, None),`;
|
|
206
398
|
} else {
|
|
207
399
|
return ` ` + camelName + `: s.fieldOr("` + name + `", S.nullableAsOption(` + schemaCode + `), None),`;
|
|
208
400
|
}
|
|
@@ -218,11 +410,42 @@ function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
|
218
410
|
note: "Mixed object/non-object intersection"
|
|
219
411
|
});
|
|
220
412
|
let fields$2 = objectProps.map(param => {
|
|
413
|
+
let fieldType = param[1];
|
|
221
414
|
let name = param[0];
|
|
222
|
-
let schemaCode = recurse(
|
|
415
|
+
let schemaCode = recurse(fieldType);
|
|
223
416
|
let camelName = CodegenUtils.escapeKeyword(JsConvertCase.toCamelCase(name));
|
|
417
|
+
let alreadyNullable = true;
|
|
418
|
+
if (!schemaCode.startsWith("S.nullableAsOption(")) {
|
|
419
|
+
let tmp;
|
|
420
|
+
if (typeof fieldType !== "object") {
|
|
421
|
+
tmp = false;
|
|
422
|
+
} else {
|
|
423
|
+
switch (fieldType.TAG) {
|
|
424
|
+
case "Union" :
|
|
425
|
+
tmp = fieldType._0.some(t => {
|
|
426
|
+
if (typeof t !== "object") {
|
|
427
|
+
return t === "Null";
|
|
428
|
+
}
|
|
429
|
+
if (t.TAG !== "Literal") {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
let tmp = t._0;
|
|
433
|
+
return typeof tmp !== "object";
|
|
434
|
+
});
|
|
435
|
+
break;
|
|
436
|
+
case "Option" :
|
|
437
|
+
tmp = true;
|
|
438
|
+
break;
|
|
439
|
+
default:
|
|
440
|
+
tmp = false;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
alreadyNullable = tmp;
|
|
444
|
+
}
|
|
224
445
|
if (param[2]) {
|
|
225
446
|
return ` ` + camelName + `: s.field("` + name + `", ` + schemaCode + `),`;
|
|
447
|
+
} else if (alreadyNullable) {
|
|
448
|
+
return ` ` + camelName + `: s.fieldOr("` + name + `", ` + schemaCode + `, None),`;
|
|
226
449
|
} else {
|
|
227
450
|
return ` ` + camelName + `: s.fieldOr("` + name + `", S.nullableAsOption(` + schemaCode + `), None),`;
|
|
228
451
|
}
|
|
@@ -230,14 +453,16 @@ function generateSchemaWithContext(ctx, depthOpt, irType) {
|
|
|
230
453
|
return `S.object(s => {\n` + fields$2 + `\n })`;
|
|
231
454
|
case "Reference" :
|
|
232
455
|
let ref = irType._0;
|
|
233
|
-
let
|
|
234
|
-
let
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
} else {
|
|
239
|
-
schemaPath = Stdlib_Option.getOr(ReferenceResolver.refToSchemaPath(ctx.insideComponentSchemas, ctx.modulePrefix, ref), "S.json");
|
|
456
|
+
let refName = ref.includes("/") ? Stdlib_Option.getOr(ref.split("/")[ref.split("/").length - 1 | 0], "") : ref;
|
|
457
|
+
let selfName = ctx.selfRefName;
|
|
458
|
+
let isSelfRef = selfName !== undefined ? refName === selfName : false;
|
|
459
|
+
if (isSelfRef) {
|
|
460
|
+
return "schema";
|
|
240
461
|
}
|
|
462
|
+
let available = ctx.availableSchemas;
|
|
463
|
+
let schemaPath = available !== undefined ? (
|
|
464
|
+
available.includes(refName) ? JsConvertCase.toPascalCase(refName) + `.schema` : `ComponentSchemas.` + JsConvertCase.toPascalCase(refName) + `.schema`
|
|
465
|
+
) : Stdlib_Option.getOr(ReferenceResolver.refToSchemaPath(ctx.insideComponentSchemas, ctx.modulePrefix, ref), "S.json");
|
|
241
466
|
if (schemaPath === "S.json") {
|
|
242
467
|
GenerationContext.addWarning(ctx, {
|
|
243
468
|
TAG: "FallbackToJson",
|
|
@@ -261,21 +486,33 @@ function generateSchema(depthOpt, pathOpt, insideComponentSchemasOpt, availableS
|
|
|
261
486
|
let path = pathOpt !== undefined ? pathOpt : "";
|
|
262
487
|
let insideComponentSchemas = insideComponentSchemasOpt !== undefined ? insideComponentSchemasOpt : false;
|
|
263
488
|
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
264
|
-
let ctx = GenerationContext.make(path, insideComponentSchemas, availableSchemas, modulePrefix, undefined);
|
|
489
|
+
let ctx = GenerationContext.make(path, insideComponentSchemas, availableSchemas, modulePrefix, undefined, undefined);
|
|
265
490
|
return [
|
|
266
|
-
generateSchemaWithContext(ctx, depth, irType),
|
|
491
|
+
generateSchemaWithContext(ctx, depth, undefined, irType),
|
|
267
492
|
ctx.warnings
|
|
268
493
|
];
|
|
269
494
|
}
|
|
270
495
|
|
|
271
|
-
function generateNamedSchema(namedSchema, insideComponentSchemasOpt, availableSchemas, modulePrefixOpt) {
|
|
496
|
+
function generateNamedSchema(namedSchema, insideComponentSchemasOpt, availableSchemas, modulePrefixOpt, extractedTypesOpt) {
|
|
272
497
|
let insideComponentSchemas = insideComponentSchemasOpt !== undefined ? insideComponentSchemasOpt : false;
|
|
273
498
|
let modulePrefix = modulePrefixOpt !== undefined ? modulePrefixOpt : "";
|
|
274
|
-
let
|
|
499
|
+
let extractedTypes = extractedTypesOpt !== undefined ? extractedTypesOpt : [];
|
|
500
|
+
let ctx = GenerationContext.make(`schema.` + namedSchema.name, insideComponentSchemas, availableSchemas, modulePrefix, undefined, undefined);
|
|
275
501
|
let d = namedSchema.description;
|
|
276
502
|
let doc = d !== undefined ? CodegenUtils.generateDocComment(undefined, d, undefined) : "";
|
|
503
|
+
let extractedTypeMap = extractedTypes.length !== 0 ? extractedTypes : undefined;
|
|
504
|
+
let mainSchema = generateSchemaWithContext(ctx, 0, extractedTypeMap, namedSchema.type_);
|
|
505
|
+
let extractedDefs = extractedTypes.map(param => {
|
|
506
|
+
let typeName = param.typeName;
|
|
507
|
+
let auxCtx = GenerationContext.make(`schema.` + typeName, insideComponentSchemas, availableSchemas, modulePrefix, undefined, undefined);
|
|
508
|
+
let filteredMap = extractedTypes.filter(param => param.typeName !== typeName);
|
|
509
|
+
let auxExtractedTypeMap = filteredMap.length !== 0 ? filteredMap : undefined;
|
|
510
|
+
let auxSchema = generateSchemaWithContext(auxCtx, 0, auxExtractedTypeMap, param.irType);
|
|
511
|
+
return `let ` + typeName + `Schema = ` + auxSchema;
|
|
512
|
+
});
|
|
513
|
+
let allDefs = extractedDefs.concat([doc + `let ` + namedSchema.name + `Schema = ` + mainSchema]);
|
|
277
514
|
return [
|
|
278
|
-
|
|
515
|
+
allDefs.join("\n\n"),
|
|
279
516
|
ctx.warnings
|
|
280
517
|
];
|
|
281
518
|
}
|
|
@@ -283,7 +520,7 @@ function generateNamedSchema(namedSchema, insideComponentSchemasOpt, availableSc
|
|
|
283
520
|
function generateAllSchemas(context) {
|
|
284
521
|
let warnings = [];
|
|
285
522
|
let schemas = Object.values(context.schemas).toSorted((a, b) => Primitive_string.compare(a.name, b.name)).map(s => {
|
|
286
|
-
let match = generateNamedSchema(s, undefined, undefined, undefined);
|
|
523
|
+
let match = generateNamedSchema(s, undefined, undefined, undefined, undefined);
|
|
287
524
|
warnings.push(...match[1]);
|
|
288
525
|
return match[0];
|
|
289
526
|
});
|