quicktype-core 23.0.100 → 23.0.102

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.
@@ -33,6 +33,14 @@ export declare class TypeScriptZodRenderer extends ConvenienceRenderer {
33
33
  protected typeMapTypeFor(t: Type, required?: boolean): Sourcelike;
34
34
  protected emitObject(name: Name, t: ObjectType): void;
35
35
  protected emitEnum(e: EnumType, enumName: Name): void;
36
+ /** Static function that extracts underlying type refs for types that form part of the
37
+ * definition of the passed type - used to ensure that these appear in generated source
38
+ * before types that reference them.
39
+ *
40
+ * Primitive types don't need defining and enums are output before other types, hence,
41
+ * these are ignored.
42
+ */
43
+ static extractUnderlyingTyperefs(type: Type): number[];
36
44
  protected emitSchemas(): void;
37
45
  protected emitSourceStructure(): void;
38
46
  }
@@ -6,6 +6,7 @@ const ConvenienceRenderer_1 = require("../ConvenienceRenderer");
6
6
  const Naming_1 = require("../Naming");
7
7
  const RendererOptions_1 = require("../RendererOptions");
8
8
  const TargetLanguage_1 = require("../TargetLanguage");
9
+ const Type_1 = require("../Type");
9
10
  const TypeUtils_1 = require("../TypeUtils");
10
11
  const Acronyms_1 = require("../support/Acronyms");
11
12
  const Strings_1 = require("../support/Strings");
@@ -114,41 +115,127 @@ class TypeScriptZodRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
114
115
  this.emitLine("export type ", enumName, " = z.infer<typeof ", enumName, "Schema>;");
115
116
  }
116
117
  }
118
+ /** Static function that extracts underlying type refs for types that form part of the
119
+ * definition of the passed type - used to ensure that these appear in generated source
120
+ * before types that reference them.
121
+ *
122
+ * Primitive types don't need defining and enums are output before other types, hence,
123
+ * these are ignored.
124
+ */
125
+ static extractUnderlyingTyperefs(type) {
126
+ let typeRefs = [];
127
+ //Ignore enums and primitives
128
+ if (!type.isPrimitive() && type.kind != "enum") {
129
+ //need to extract constituent types for unions and intersections (which both extend SetOperationType)
130
+ //and can ignore the union/intersection itself
131
+ if (type instanceof Type_1.SetOperationType) {
132
+ type.members.forEach(member => {
133
+ //recurse as the underlying type could itself be a union, instersection or array etc.
134
+ typeRefs.push(...TypeScriptZodRenderer.extractUnderlyingTyperefs(member));
135
+ });
136
+ }
137
+ //need to extract additional properties for object, class and map types (which all extend ObjectType)
138
+ if (type instanceof Type_1.ObjectType) {
139
+ const addType = type.getAdditionalProperties();
140
+ if (addType) {
141
+ //recurse as the underlying type could itself be a union, instersection or array etc.
142
+ typeRefs.push(...TypeScriptZodRenderer.extractUnderlyingTyperefs(addType));
143
+ }
144
+ }
145
+ //need to extract items types for ArrayType
146
+ if (type instanceof Type_1.ArrayType) {
147
+ const itemsType = type.items;
148
+ if (itemsType) {
149
+ //recurse as the underlying type could itself be a union, instersection or array etc.
150
+ typeRefs.push(...TypeScriptZodRenderer.extractUnderlyingTyperefs(itemsType));
151
+ }
152
+ }
153
+ //Finally return the reference to a class as that will need to be defined (where objects, maps, unions, intersections and arrays do not)
154
+ if (type instanceof Type_1.ClassType) {
155
+ typeRefs.push(type.typeRef);
156
+ }
157
+ }
158
+ return typeRefs;
159
+ }
117
160
  emitSchemas() {
118
161
  this.ensureBlankLine();
119
162
  this.forEachEnum("leading-and-interposing", (u, enumName) => {
120
163
  this.emitEnum(u, enumName);
121
164
  });
165
+ // All children must be defined before this type to avoid forward references in generated code
166
+ // Build a model that will tell us if a referenced type has been defined then make multiple
167
+ // passes over the defined objects to put them into the correct order for output in the
168
+ // generated sourcecode
122
169
  const order = [];
123
- const mapKey = [];
124
- const mapValue = [];
170
+ const mapType = [];
171
+ const mapTypeRef = [];
172
+ const mapName = [];
173
+ const mapChildTypeRefs = [];
125
174
  this.forEachObject("none", (type, name) => {
126
- mapKey.push(name);
127
- mapValue.push(this.gatherSource(() => this.emitObject(name, type)));
175
+ mapType.push(type);
176
+ mapTypeRef.push(type.typeRef);
177
+ mapName.push(name);
178
+ const children = type.getChildren();
179
+ let childTypeRefs = [];
180
+ children.forEach(child => {
181
+ childTypeRefs = childTypeRefs.concat(TypeScriptZodRenderer.extractUnderlyingTyperefs(child));
182
+ });
183
+ mapChildTypeRefs.push(childTypeRefs);
128
184
  });
129
- mapKey.forEach((_, index) => {
130
- // assume first
131
- let ordinal = 0;
132
- // pull out all names
133
- const source = mapValue[index];
134
- const names = source.filter(value => value);
135
- // must be behind all these names
136
- for (let i = 0; i < names.length; i++) {
137
- const depName = names[i];
138
- // find this name's ordinal, if it has already been added
139
- for (let j = 0; j < order.length; j++) {
140
- const depIndex = order[j];
141
- if (mapKey[depIndex] === depName) {
142
- // this is the index of the dependency, so make sure we come after it
143
- ordinal = Math.max(ordinal, depIndex + 1);
185
+ //Items to process on this pass
186
+ let indices = [];
187
+ mapType.forEach((_, index) => {
188
+ indices.push(index);
189
+ });
190
+ //items to process on the next pass
191
+ let deferredIndices = [];
192
+ //defensive: make sure we don't loop forever, even complex sets shouldn't require many passes
193
+ const MAX_PASSES = 999;
194
+ let passNum = 0;
195
+ do {
196
+ indices.forEach(index => {
197
+ // must be behind all these children
198
+ const childTypeRefs = mapChildTypeRefs[index];
199
+ let foundAllChildren = true;
200
+ childTypeRefs.forEach(childRef => {
201
+ //defensive: first check if there is a definition for the referenced type (there should be)
202
+ if (mapTypeRef.indexOf(childRef) > -1) {
203
+ let found = false;
204
+ // find this childs's ordinal, if it has already been added
205
+ //faster to go through what we've defined so far than all definitions
206
+ for (let j = 0; j < order.length; j++) {
207
+ const childIndex = order[j];
208
+ if (mapTypeRef[childIndex] === childRef) {
209
+ found = true;
210
+ break;
211
+ }
212
+ }
213
+ foundAllChildren = foundAllChildren && found;
214
+ }
215
+ else {
216
+ console.error("A child type reference was not found amongst all Object definitions! TypeRef: " + childRef);
144
217
  }
218
+ });
219
+ if (foundAllChildren) {
220
+ // insert index into order as we are safe to define this type
221
+ order.push(index);
145
222
  }
223
+ else {
224
+ //defer to a subsequent pass as we need to define other types
225
+ deferredIndices.push(index);
226
+ }
227
+ });
228
+ indices = deferredIndices;
229
+ deferredIndices = [];
230
+ passNum++;
231
+ if (passNum > MAX_PASSES) {
232
+ //giving up
233
+ order.push(...deferredIndices);
234
+ console.warn("Exceeded maximum number of passes when determining output order, output may contain forward references");
146
235
  }
147
- // insert index
148
- order.splice(ordinal, 0, index);
149
- });
236
+ } while (indices.length > 0 && passNum <= MAX_PASSES);
150
237
  // now emit ordered source
151
- order.forEach(i => this.emitGatheredSource(mapValue[i]));
238
+ order.forEach(i => this.emitGatheredSource(this.gatherSource(() => this.emitObject(mapName[i], mapType[i]))));
152
239
  }
153
240
  emitSourceStructure() {
154
241
  if (this.leadingComments !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quicktype-core",
3
- "version": "23.0.100",
3
+ "version": "23.0.102",
4
4
  "description": "The quicktype engine as a library",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",