forked from vega/ts-json-schema-generator
/
removeUnreachable.ts
89 lines (78 loc) · 2.92 KB
/
removeUnreachable.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { JSONSchema7Definition } from "json-schema";
import { Definition } from "../Schema/Definition";
import { StringMap } from "./StringMap";
const DEFINITION_OFFSET = "#/definitions/".length;
function addReachable(
definition: Definition | JSONSchema7Definition,
definitions: StringMap<Definition>,
reachable: Set<string>
) {
if (typeof definition === "boolean") {
return;
}
if (definition.$ref) {
const typeName = decodeURIComponent(definition.$ref.slice(DEFINITION_OFFSET));
if (reachable.has(typeName) || !isLocalRef(definition.$ref)) {
// we've already processed this definition, or this definition refers to an external schema
return;
}
reachable.add(typeName);
const refDefinition = definitions[typeName];
if (!refDefinition) {
throw new Error(`Encountered a reference to a missing definition: "${definition.$ref}". This is a bug.`);
}
addReachable(refDefinition, definitions, reachable);
} else if (definition.anyOf) {
for (const def of definition.anyOf) {
addReachable(def, definitions, reachable);
}
} else if (definition.allOf) {
for (const def of definition.allOf) {
addReachable(def, definitions, reachable);
}
} else if (definition.oneOf) {
for (const def of definition.oneOf) {
addReachable(def, definitions, reachable);
}
} else if (definition.not) {
addReachable(definition.not, definitions, reachable);
} else if (definition.type?.includes("object")) {
for (const prop in definition.properties || {}) {
const propDefinition = definition.properties![prop];
addReachable(propDefinition, definitions, reachable);
}
const additionalProperties = definition.additionalProperties;
if (additionalProperties) {
addReachable(additionalProperties, definitions, reachable);
}
} else if (definition.type?.includes("array")) {
const items = definition.items;
if (Array.isArray(items)) {
for (const item of items) {
addReachable(item, definitions, reachable);
}
} else if (items) {
addReachable(items, definitions, reachable);
}
} else if (definition.then) {
addReachable(definition.then, definitions, reachable);
}
}
export function removeUnreachable(
rootTypeDefinition: Definition | undefined,
definitions: StringMap<Definition>
): StringMap<Definition> {
if (!rootTypeDefinition) {
return definitions;
}
const reachable = new Set<string>();
addReachable(rootTypeDefinition, definitions, reachable);
const out: StringMap<Definition> = {};
for (const def of reachable) {
out[def] = definitions[def];
}
return out;
}
function isLocalRef(ref: string) {
return ref.charAt(0) === "#";
}