@@ -16,74 +16,80 @@ function remove(a: Set<unknown>, b: Set<unknown>) {
1616 }
1717}
1818
19+ const getAllSourcesFromSchema = ( it : IRModel ) => {
20+ const allSources : Array < MaybeIRModel > = [ it as MaybeIRModel ]
21+
22+ if ( it . type === "object" ) {
23+ if ( it . oneOf . length > 0 ) {
24+ allSources . push ( ...it . oneOf )
25+ }
26+
27+ if ( it . allOf . length > 0 ) {
28+ allSources . push ( ...it . allOf )
29+ }
30+
31+ if ( it . anyOf . length > 0 ) {
32+ allSources . push ( ...it . anyOf )
33+ }
34+
35+ if (
36+ it . additionalProperties &&
37+ typeof it . additionalProperties !== "boolean"
38+ ) {
39+ allSources . push ( it . additionalProperties )
40+ }
41+ } else if ( it . type === "array" ) {
42+ allSources . push ( it . items )
43+ }
44+
45+ return allSources
46+ }
47+
1948const getDependenciesFromSchema = (
2049 schema : IRModel ,
2150 getNameForRef : ( ref : Reference ) => string ,
2251) : Set < string > => {
23- const allSources : Array < MaybeIRModel > = [ schema as MaybeIRModel ]
24- . concat ( Reflect . get ( schema , "oneOf" ) ?? [ ] )
25- . concat ( Reflect . get ( schema , "allOf" ) ?? [ ] )
26- . concat ( Reflect . get ( schema , "anyOf" ) ?? [ ] )
27- . concat (
28- schema . type === "object" &&
29- schema . additionalProperties &&
30- typeof schema . additionalProperties !== "boolean"
31- ? [ schema . additionalProperties ]
32- : [ ] ,
33- )
34- . concat ( schema . type === "array" ? [ schema . items ] : [ ] )
52+ const allSources = getAllSourcesFromSchema ( schema )
53+ const result = new Set < string > ( )
3554
36- return allSources . reduce ( ( acc , it ) => {
55+ for ( const it of allSources ) {
3756 if ( isRef ( it ) ) {
38- acc . add ( getNameForRef ( it ) )
57+ result . add ( getNameForRef ( it ) )
3958 } else if ( it . type === "object" ) {
40- // biome-ignore lint/complexity/noForEach: <explanation>
41- Object . values ( it . properties )
42- . concat ( Reflect . get ( schema , "oneOf" ) ?? [ ] )
43- . concat ( Reflect . get ( schema , "allOf" ) ?? [ ] )
44- . concat ( Reflect . get ( schema , "anyOf" ) ?? [ ] )
45- . forEach ( ( prop ) => {
46- if ( isRef ( prop ) ) {
47- acc . add ( getNameForRef ( prop ) )
48- } else if ( prop . type === "object" ) {
49- intersect ( acc , getDependenciesFromSchema ( prop , getNameForRef ) )
50- } else if ( prop . type === "array" ) {
51- if ( isRef ( prop . items ) ) {
52- acc . add ( getNameForRef ( prop . items ) )
53- } else {
54- intersect (
55- acc ,
56- getDependenciesFromSchema ( prop . items , getNameForRef ) ,
57- )
58- }
59+ const innerSources = Object . values ( it . properties )
60+
61+ if ( it . oneOf . length > 0 ) {
62+ innerSources . push ( ...it . oneOf )
63+ }
64+
65+ if ( it . allOf . length > 0 ) {
66+ innerSources . push ( ...it . allOf )
67+ }
68+
69+ if ( it . anyOf . length > 0 ) {
70+ innerSources . push ( ...it . anyOf )
71+ }
72+
73+ for ( const prop of innerSources ) {
74+ if ( isRef ( prop ) ) {
75+ result . add ( getNameForRef ( prop ) )
76+ } else if ( prop . type === "object" ) {
77+ intersect ( result , getDependenciesFromSchema ( prop , getNameForRef ) )
78+ } else if ( prop . type === "array" ) {
79+ if ( isRef ( prop . items ) ) {
80+ result . add ( getNameForRef ( prop . items ) )
81+ } else {
82+ intersect (
83+ result ,
84+ getDependenciesFromSchema ( prop . items , getNameForRef ) ,
85+ )
5986 }
60- } )
87+ }
88+ }
6189 }
90+ }
6291
63- return acc
64- } , new Set < string > ( ) )
65- }
66-
67- function split < T > (
68- arr : T [ ] ,
69- predicate : ( it : T ) => "left" | "right" ,
70- ) : [ T [ ] , T [ ] ] {
71- const left : T [ ] = [ ]
72- const right : T [ ] = [ ]
73-
74- // biome-ignore lint/complexity/noForEach: <explanation>
75- arr . forEach ( ( it ) => {
76- switch ( predicate ( it ) ) {
77- case "left" :
78- left . push ( it )
79- break
80- case "right" :
81- right . push ( it )
82- break
83- }
84- } )
85-
86- return [ left , right ]
92+ return result
8793}
8894
8995export type DependencyGraph = { order : string [ ] ; circular : Set < string > }
@@ -106,19 +112,16 @@ export function buildDependencyGraph(
106112 /**
107113 * Create an object mapping name -> Set(direct dependencies)
108114 */
109- const dependencyGraph = Object . fromEntries (
110- // TODO: this may miss extracted in-line schemas
111- Object . entries ( input . allSchemas ( ) ) . map ( ( [ name , schema ] ) => {
112- return [
113- getNameForRef ( { $ref : name } ) ,
114- new Set ( getDependenciesFromSchema ( schema , getNameForRef ) ) ,
115- ]
116- } ) ,
117- )
118-
115+ const remaining = new Map < string , Set < string > > ( )
119116 const order : string [ ] = [ ]
120117
121- let remaining = { ...dependencyGraph }
118+ // TODO: this may miss extracted in-line schemas
119+ for ( const [ name , schema ] of Object . entries ( input . allSchemas ( ) ) ) {
120+ remaining . set (
121+ getNameForRef ( { $ref : name } ) ,
122+ getDependenciesFromSchema ( schema , getNameForRef ) ,
123+ )
124+ }
122125
123126 /**
124127 * While objects with no dependencies remain,
@@ -130,14 +133,22 @@ export function buildDependencyGraph(
130133 * all the remaining objects reference each other circularly in some
131134 * way, and therefore can not be ordered correctly at all.
132135 */
133- while ( Object . keys ( remaining ) . length ) {
134- const [ left , right ] = split ( Object . entries ( remaining ) , ( [ , deps ] ) =>
135- deps . size === 0 ? "left" : "right" ,
136- )
137-
138- const noDeps = left . map ( ( it ) => it [ 0 ] ) . sort ( )
136+ while ( remaining . size > 0 ) {
137+ const noDeps = [ ]
138+ const noDepsSet = new Set ( )
139+
140+ const hasDeps : [ string , Set < string > ] [ ] = [ ]
141+
142+ for ( const [ name , deps ] of remaining ) {
143+ if ( deps . size === 0 ) {
144+ noDeps . push ( name )
145+ noDepsSet . add ( name )
146+ } else {
147+ hasDeps . push ( [ name , deps ] )
148+ }
149+ }
139150
140- const noDepsSet = new Set ( noDeps )
151+ noDeps . sort ( )
141152
142153 order . push ( ...noDeps )
143154
@@ -146,13 +157,14 @@ export function buildDependencyGraph(
146157 break
147158 }
148159
149- remaining = Object . fromEntries ( right )
160+ for ( const name of noDeps ) {
161+ remaining . delete ( name )
162+ }
150163
151- // biome-ignore lint/complexity/noForEach: <explanation>
152- right . forEach ( ( [ , deps ] ) => {
164+ for ( const [ , deps ] of hasDeps ) {
153165 remove ( deps , noDepsSet )
154- } )
166+ }
155167 }
156168
157- return { order, circular : new Set ( Object . keys ( remaining ) ) }
169+ return { order, circular : new Set ( remaining . keys ( ) ) }
158170}
0 commit comments