Skip to content

Commit b220658

Browse files
authored
refactor: tidy up dependency-graph (#335)
1 parent 61d1acd commit b220658

1 file changed

Lines changed: 93 additions & 81 deletions

File tree

packages/openapi-code-generator/src/core/dependency-graph.ts

Lines changed: 93 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
1948
const 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

8995
export 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

Comments
 (0)