@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.

@@ -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
- let parts = irType._0.split("/");
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 dependencyMap = Stdlib_Array.reduce(schemas, {}, (acc, schema) => {
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
- acc[schema.name] = references;
68
- return acc;
68
+ return references.map(dep => [
69
+ schema.name,
70
+ dep
71
+ ]);
69
72
  });
70
- let sortedSchemas = [];
71
- let inDegreeMap = Stdlib_Array.reduce(schemas, {}, (acc, schema) => {
72
- let degree = Stdlib_Option.mapOr(dependencyMap[schema.name], 0, prim => prim.length);
73
- acc[schema.name] = degree;
74
- return acc;
75
- });
76
- let queue = schemas.filter(schema => Stdlib_Option.getOr(inDegreeMap[schema.name], 0) === 0);
77
- while (queue.length !== 0) {
78
- let v = queue.shift();
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
- let currentDegree = Stdlib_Option.getOr(inDegreeMap[otherSchema.name], 0);
87
- let newDegree = currentDegree - 1 | 0;
88
- inDegreeMap[otherSchema.name] = newDegree;
89
- if (newDegree === 0) {
90
- queue.push(otherSchema);
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
- let sortedNames = sortedSchemas.map(s => s.name);
96
- let remainingSchemas = schemas.filter(s => !sortedNames.some(name => name === s.name)).toSorted((a, b) => Primitive_string.compare(a.name, b.name));
97
- let finalSortedSchemas = sortedSchemas.concat(remainingSchemas);
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 typeCtx = GenerationContext.make(`ComponentSchemas.` + schema.name, true, availableSchemaNames, undefined, undefined);
102
- let schemaCtx = GenerationContext.make(`ComponentSchemas.` + schema.name, true, availableSchemaNames, undefined, undefined);
103
- let typeCode = IRToTypeGenerator.generateTypeWithContext(typeCtx, 0, schema.type_);
104
- let schemaCode = IRToSuryGenerator.generateSchemaWithContext(schemaCtx, 0, schema.type_);
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
- type t = ` + typeCode + `
110
- let schema = ` + schemaCode + `
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
- /* FileSystem Not a pure module */
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
- ) : "~body as _";
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 + `, ~fetch: ` + CodegenUtils.fetchTypeSignature + `): promise<` + functionName + `Response> => {
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(param[1]);
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 = match[0] && match[1] && effectiveTypes.length === 2 && SchemaIR.equals(Stdlib_Option.getOr(arrayItemType, "Unknown"), Stdlib_Option.getOr(match[3], "Unknown")) ? `S.array(` + recurse(Stdlib_Option.getOr(arrayItemType, "Unknown")) + `)` : (
141
- effectiveTypes.every(t => {
142
- if (typeof t !== "object") {
143
- return false;
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
- if (t.TAG !== "Literal") {
146
- return false;
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
- let tmp = t._0;
149
- if (typeof tmp !== "object") {
150
- return false;
151
- } else {
152
- return tmp.TAG === "StringLiteral";
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
- }) && effectiveTypes.length !== 0 && effectiveTypes.length <= 50 ? `S.union([` + effectiveTypes.map(recurse).join(", ") + `])` : (
155
- effectiveTypes.length !== 0 ? `S.union([` + effectiveTypes.map(recurse).join(", ") + `])` : "S.json"
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(param[1]);
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(param[1]);
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 available = ctx.availableSchemas;
234
- let schemaPath;
235
- if (available !== undefined) {
236
- let name = Stdlib_Option.getOr(ref.split("/")[ref.split("/").length - 1 | 0], "");
237
- schemaPath = available.includes(name) ? JsConvertCase.toPascalCase(name) + `.schema` : `ComponentSchemas.` + JsConvertCase.toPascalCase(name) + `.schema`;
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 ctx = GenerationContext.make(`schema.` + namedSchema.name, insideComponentSchemas, availableSchemas, modulePrefix, undefined);
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
- doc + `let ` + namedSchema.name + `Schema = ` + generateSchemaWithContext(ctx, 0, namedSchema.type_),
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
  });