@@ -78,15 +78,21 @@ type DiscriminatorState = {
7878} ;
7979
8080class VisitorNode < NodeType extends VisitorTypes > {
81- public discriminator : DiscriminatorState = { } ;
8281 public originalRef ?: string ;
82+ public traversedObjects : Set < OpenAPIObject > ; // track circular references
83+ public discriminator : DiscriminatorState = { } ;
8384
8485 constructor (
8586 public type : NodeType ,
8687 public object : VisitorObjects [ NodeType ] | undefined ,
87- public path : string [ ] ,
88+ traversedObjects : Set < OpenAPIObject > = new Set ( ) ,
89+ public path : string [ ] = [ ] ,
8890 public pathFromParent ?: string [ ] ,
89- ) { }
91+ ) {
92+ // copy traversed object to not affect other children of the same parent
93+ this . traversedObjects = new Set ( traversedObjects ) ;
94+ this . traversedObjects . add ( object ) ;
95+ }
9096
9197 static fromParent <
9298 ParentType extends VisitorTypes ,
@@ -105,6 +111,7 @@ class VisitorNode<NodeType extends VisitorTypes> {
105111 return new VisitorNode (
106112 type ,
107113 parent . object [ propertyPath ] as unknown as VisitorObjects [ NodeType ] ,
114+ parent . traversedObjects ,
108115 [ ...parent . path , propertyPath ] ,
109116 ) ;
110117 }
@@ -135,6 +142,7 @@ class VisitorNode<NodeType extends VisitorTypes> {
135142 new VisitorNode (
136143 type ,
137144 value ,
145+ parent . traversedObjects ,
138146 [ ...parent . path , dictPath , key ] ,
139147 [ dictPath , key ] ,
140148 ) ,
@@ -170,6 +178,7 @@ class VisitorNode<NodeType extends VisitorTypes> {
170178 new VisitorNode (
171179 type ,
172180 value ,
181+ parent . traversedObjects ,
173182 [ ...parent . path , arrayPath , `${ index } ` ] ,
174183 [ arrayPath , `${ index } ` ] ,
175184 ) ,
@@ -208,7 +217,7 @@ export class SchemaPreprocessor<
208217 }
209218
210219 public preProcess ( ) : { apiDoc : OpenAPISchema ; apiDocRes : OpenAPISchema } {
211- const root = new VisitorNode ( 'document' , this . apiDoc , [ ] ) ;
220+ const root = new VisitorNode ( 'document' , this . apiDoc ) ;
212221
213222 this . traverseSchema ( root ) ;
214223
@@ -234,23 +243,33 @@ export class SchemaPreprocessor<
234243 return ;
235244 }
236245
237- if ( seenObjects . has ( node . object ) ) return ;
238-
246+ // resolve references
239247 if ( isReferenceNode ( node ) && isReferenceObject ( node . object ) ) {
240248 node . originalRef = node . object . $ref ;
241249
250+ // TODO: Seemingly we do not want to "unreference" these schema properties.
251+ // Find way to implement this more elegantly.
252+ if (
253+ node . pathFromParent &&
254+ [ 'allOf' , 'oneOf' , 'anyOf' ] . includes ( node . pathFromParent [ 0 ] ) &&
255+ hasNodeType ( node , 'schema' )
256+ ) {
257+ this . processDiscriminator (
258+ hasNodeType ( parent , 'schema' ) ? parent : undefined ,
259+ node ,
260+ ) ;
261+ return ;
262+ }
263+
242264 const resolvedObject = this . resolveObject < typeof node . type > (
243265 node . object ,
244266 ) ;
245267
246- if ( seenObjects . has ( resolvedObject ) ) {
247- if ( hasNodeType ( node , 'schema' ) ) {
248- this . processDiscriminator (
249- hasNodeType ( parent , 'schema' ) ? parent : undefined ,
250- node ,
251- ) ;
252- }
253-
268+ // stop when detecting circular references
269+ if (
270+ resolvedObject === undefined ||
271+ node . traversedObjects . has ( resolvedObject )
272+ ) {
254273 return ;
255274 }
256275
@@ -271,10 +290,9 @@ export class SchemaPreprocessor<
271290 }
272291
273292 node . object = resolvedObject ;
274-
275- return traverse ( parent , node as VisitorNode < NodeType > , state ) ;
276293 }
277294
295+ if ( seenObjects . has ( node . object ) ) return ;
278296 seenObjects . add ( node . object ) ;
279297
280298 this . visitNode ( parent , node , state ) ;
@@ -748,7 +766,14 @@ export class SchemaPreprocessor<
748766 ...VisitorNode . fromParentDict ( parent , 'pathItem' , 'pathItems' ) ,
749767 ) ;
750768 // process components V3.1 also like normal components
751- children . push ( new VisitorNode ( 'components' , parent . object , parent . path ) ) ;
769+ children . push (
770+ new VisitorNode (
771+ 'components' ,
772+ parent . object ,
773+ parent . traversedObjects ,
774+ parent . path ,
775+ ) ,
776+ ) ;
752777
753778 return children ;
754779 }
@@ -786,10 +811,12 @@ export class SchemaPreprocessor<
786811 if ( typeof parent . object . additionalProperties !== 'boolean' ) {
787812 // constructing this manually, as the type of additional properties includes boolean
788813 children . push (
789- new VisitorNode ( 'schema' , parent . object . additionalProperties , [
790- ...parent . path ,
791- 'additionalProperties' ,
792- ] ) ,
814+ new VisitorNode (
815+ 'schema' ,
816+ parent . object . additionalProperties ,
817+ parent . traversedObjects ,
818+ [ ...parent . path , 'additionalProperties' ] ,
819+ ) ,
793820 ) ;
794821 }
795822 children . push (
@@ -933,7 +960,10 @@ export class SchemaPreprocessor<
933960
934961 forEachValue ( parent . object , ( pathItem , key ) => {
935962 children . push (
936- new VisitorNode ( 'pathItem' , pathItem , [ ...parent . path , key ] ) ,
963+ new VisitorNode ( 'pathItem' , pathItem , parent . traversedObjects , [
964+ ...parent . path ,
965+ key ,
966+ ] ) ,
937967 ) ;
938968 } ) ;
939969
0 commit comments