forked from stoplightio/spectral
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresolved.ts
124 lines (102 loc) · 3.88 KB
/
resolved.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { extractSourceFromRef, hasRef, isLocalRef } from '@stoplight/json';
import { IGraphNodeData, IResolveError } from '@stoplight/json-ref-resolver/types';
import { normalize, resolve } from '@stoplight/path';
import { Dictionary, ILocation, IRange, JsonPath } from '@stoplight/types';
import { DepGraph } from 'dependency-graph';
import { get } from 'lodash';
import { IParsedResult, ResolveResult } from './types';
import { getClosestJsonPath, getEndRef, isAbsoluteRef, safePointerToPath, traverseObjUntilRef } from './utils';
export const getDefaultRange = (): IRange => ({
start: {
line: 0,
character: 0,
},
end: {
line: 0,
character: 0,
},
});
export class Resolved {
public readonly refMap: Dictionary<string>;
public readonly graph: DepGraph<IGraphNodeData>;
public readonly resolved: unknown;
public readonly unresolved: unknown;
public readonly errors: IResolveError[];
public formats?: string[] | null;
public get source() {
return this.parsed.source ? normalize(this.parsed.source) : this.parsed.source;
}
constructor(
public readonly parsed: IParsedResult,
resolveResult: ResolveResult,
public parsedRefs: Dictionary<IParsedResult>,
) {
this.unresolved = parsed.parsed.data;
this.formats = parsed.formats;
this.refMap = resolveResult.refMap;
this.graph = resolveResult.graph;
this.resolved = resolveResult.result;
this.errors = resolveResult.errors;
}
public getParsedForJsonPath(path: JsonPath) {
try {
const newPath: JsonPath = getClosestJsonPath(this.resolved, path);
let $ref = traverseObjUntilRef(this.unresolved, newPath);
if ($ref === null) {
return {
path: getClosestJsonPath(this.unresolved, path),
doc: this.parsed,
missingPropertyPath: path,
};
}
const missingPropertyPath =
newPath.length === 0 ? [] : path.slice(path.lastIndexOf(newPath[newPath.length - 1]) + 1);
let { source } = this;
while (true) {
if (source === void 0) return null;
$ref = getEndRef(this.graph.getNodeData(source).refMap, $ref);
if ($ref === null) return null;
const scopedPath = [...safePointerToPath($ref), ...newPath];
let resolvedDoc;
if (isLocalRef($ref)) {
resolvedDoc = source === this.parsed.source ? this.parsed : this.parsedRefs[source];
} else {
const extractedSource = extractSourceFromRef($ref)!;
source = isAbsoluteRef(extractedSource) ? extractedSource : resolve(source, '..', extractedSource);
resolvedDoc = source === this.parsed.source ? this.parsed : this.parsedRefs[source];
const { parsed } = resolvedDoc;
const obj = scopedPath.length === 0 || hasRef(parsed.data) ? parsed.data : get(parsed.data, scopedPath);
if (hasRef(obj)) {
$ref = obj.$ref;
continue;
}
}
const closestPath = getClosestJsonPath(resolvedDoc.parsed.data, scopedPath);
return {
doc: resolvedDoc,
path: closestPath,
missingPropertyPath: [...closestPath, ...missingPropertyPath],
};
}
} catch {
return null;
}
}
public getLocationForJsonPath(path: JsonPath, closest?: boolean): ILocation {
const parsedResult = this.getParsedForJsonPath(path);
if (parsedResult === null) {
return {
range: getDefaultRange(),
};
}
const location = parsedResult.doc.getLocationForJsonPath(parsedResult.doc.parsed, parsedResult.path, closest);
return {
...(parsedResult.doc.source && { uri: parsedResult.doc.source }),
range: location?.range || getDefaultRange(),
};
}
public getValueForJsonPath(path: JsonPath): unknown {
const parsedResult = this.getParsedForJsonPath(path);
return parsedResult === null ? void 0 : get(parsedResult.doc.parsed.data, parsedResult.path);
}
}