@@ -16,7 +16,7 @@ module.exports = dereference;
16
16
*/
17
17
function dereference ( parser , options ) {
18
18
// console.log('Dereferencing $ref pointers in %s', parser.$refs._root$Ref.path);
19
- let dereferenced = crawl ( parser . schema , parser . $refs . _root$Ref . path , "#" , [ ] , parser . $refs , options ) ;
19
+ let dereferenced = crawl ( parser . schema , parser . $refs . _root$Ref . path , "#" , [ ] , [ ] , { } , parser . $refs , options ) ;
20
20
parser . $refs . circular = dereferenced . circular ;
21
21
parser . schema = dereferenced . value ;
22
22
}
@@ -28,60 +28,65 @@ function dereference (parser, options) {
28
28
* @param {string } path - The full path of `obj`, possibly with a JSON Pointer in the hash
29
29
* @param {string } pathFromRoot - The path of `obj` from the schema root
30
30
* @param {object[] } parents - An array of the parent objects that have already been dereferenced
31
+ * @param {object[] } processedObjects - An array of all the objects that have already been processed
32
+ * @param {object } dereferencedCache - An map of all the dereferenced objects
31
33
* @param {$Refs } $refs
32
34
* @param {$RefParserOptions } options
33
35
* @returns {{value: object, circular: boolean} }
34
36
*/
35
- function crawl ( obj , path , pathFromRoot , parents , $refs , options ) {
37
+ function crawl ( obj , path , pathFromRoot , parents , processedObjects , dereferencedCache , $refs , options ) {
36
38
let dereferenced ;
37
39
let result = {
38
40
value : obj ,
39
41
circular : false
40
42
} ;
41
43
42
- if ( obj && typeof obj === "object" && ! ArrayBuffer . isView ( obj ) ) {
43
- parents . push ( obj ) ;
44
+ if ( options . dereference . circular === "ignore" || processedObjects . indexOf ( obj ) === - 1 ) {
45
+ if ( obj && typeof obj === "object" && ! ArrayBuffer . isView ( obj ) ) {
46
+ parents . push ( obj ) ;
47
+ processedObjects . push ( obj ) ;
44
48
45
- if ( $Ref . isAllowed$Ref ( obj , options ) ) {
46
- dereferenced = dereference$Ref ( obj , path , pathFromRoot , parents , $refs , options ) ;
47
- result . circular = dereferenced . circular ;
48
- result . value = dereferenced . value ;
49
- }
50
- else {
51
- for ( let key of Object . keys ( obj ) ) {
52
- let keyPath = Pointer . join ( path , key ) ;
53
- let keyPathFromRoot = Pointer . join ( pathFromRoot , key ) ;
54
- let value = obj [ key ] ;
55
- let circular = false ;
56
-
57
- if ( $Ref . isAllowed$Ref ( value , options ) ) {
58
- dereferenced = dereference$Ref ( value , keyPath , keyPathFromRoot , parents , $refs , options ) ;
59
- circular = dereferenced . circular ;
60
- // Avoid pointless mutations; breaks frozen objects to no profit
61
- if ( obj [ key ] !== dereferenced . value ) {
62
- obj [ key ] = dereferenced . value ;
63
- }
64
- }
65
- else {
66
- if ( parents . indexOf ( value ) === - 1 ) {
67
- dereferenced = crawl ( value , keyPath , keyPathFromRoot , parents , $refs , options ) ;
49
+ if ( $Ref . isAllowed$Ref ( obj , options ) ) {
50
+ dereferenced = dereference$Ref ( obj , path , pathFromRoot , parents , processedObjects , dereferencedCache , $refs , options ) ;
51
+ result . circular = dereferenced . circular ;
52
+ result . value = dereferenced . value ;
53
+ }
54
+ else {
55
+ for ( let key of Object . keys ( obj ) ) {
56
+ let keyPath = Pointer . join ( path , key ) ;
57
+ let keyPathFromRoot = Pointer . join ( pathFromRoot , key ) ;
58
+ let value = obj [ key ] ;
59
+ let circular = false ;
60
+
61
+ if ( $Ref . isAllowed$Ref ( value , options ) ) {
62
+ dereferenced = dereference$Ref ( value , keyPath , keyPathFromRoot , parents , processedObjects , dereferencedCache , $refs , options ) ;
68
63
circular = dereferenced . circular ;
69
64
// Avoid pointless mutations; breaks frozen objects to no profit
70
65
if ( obj [ key ] !== dereferenced . value ) {
71
66
obj [ key ] = dereferenced . value ;
72
67
}
73
68
}
74
69
else {
75
- circular = foundCircularReference ( keyPath , $refs , options ) ;
70
+ if ( parents . indexOf ( value ) === - 1 ) {
71
+ dereferenced = crawl ( value , keyPath , keyPathFromRoot , parents , processedObjects , dereferencedCache , $refs , options ) ;
72
+ circular = dereferenced . circular ;
73
+ // Avoid pointless mutations; breaks frozen objects to no profit
74
+ if ( obj [ key ] !== dereferenced . value ) {
75
+ obj [ key ] = dereferenced . value ;
76
+ }
77
+ }
78
+ else {
79
+ circular = foundCircularReference ( keyPath , $refs , options ) ;
80
+ }
76
81
}
77
- }
78
82
79
- // Set the "isCircular" flag if this or any other property is circular
80
- result . circular = result . circular || circular ;
83
+ // Set the "isCircular" flag if this or any other property is circular
84
+ result . circular = result . circular || circular ;
85
+ }
81
86
}
82
- }
83
87
84
- parents . pop ( ) ;
88
+ parents . pop ( ) ;
89
+ }
85
90
}
86
91
87
92
return result ;
@@ -94,14 +99,38 @@ function crawl (obj, path, pathFromRoot, parents, $refs, options) {
94
99
* @param {string } path - The full path of `$ref`, possibly with a JSON Pointer in the hash
95
100
* @param {string } pathFromRoot - The path of `$ref` from the schema root
96
101
* @param {object[] } parents - An array of the parent objects that have already been dereferenced
102
+ * @param {object[] } processedObjects - An array of all the objects that have already been dereferenced
103
+ * @param {object } dereferencedCache - An map of all the dereferenced objects
97
104
* @param {$Refs } $refs
98
105
* @param {$RefParserOptions } options
99
106
* @returns {{value: object, circular: boolean} }
100
107
*/
101
- function dereference$Ref ( $ref , path , pathFromRoot , parents , $refs , options ) {
108
+ function dereference$Ref ( $ref , path , pathFromRoot , parents , processedObjects , dereferencedCache , $refs , options ) {
102
109
// console.log('Dereferencing $ref pointer "%s" at %s', $ref.$ref, path);
103
110
104
111
let $refPath = url . resolve ( path , $ref . $ref ) ;
112
+
113
+ if ( dereferencedCache [ $refPath ] ) {
114
+ const cache = dereferencedCache [ $refPath ] ;
115
+
116
+ const refKeys = Object . keys ( $ref ) ;
117
+ if ( refKeys . length > 1 ) {
118
+ const extraKeys = { } ;
119
+ for ( let key of refKeys ) {
120
+ if ( key !== "$ref" && ! ( key in cache . value ) ) {
121
+ extraKeys [ key ] = $ref [ key ] ;
122
+ }
123
+ }
124
+ return {
125
+ circular : cache . circular ,
126
+ value : Object . assign ( { } , cache . value , extraKeys ) ,
127
+ } ;
128
+ }
129
+
130
+ return cache ;
131
+ }
132
+
133
+
105
134
let pointer = $refs . _resolve ( $refPath , path , options ) ;
106
135
107
136
if ( pointer === null ) {
@@ -122,7 +151,7 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
122
151
// Crawl the dereferenced value (unless it's circular)
123
152
if ( ! circular ) {
124
153
// Determine if the dereferenced value is circular
125
- let dereferenced = crawl ( dereferencedValue , pointer . path , pathFromRoot , parents , $refs , options ) ;
154
+ let dereferenced = crawl ( dereferencedValue , pointer . path , pathFromRoot , parents , processedObjects , dereferencedCache , $refs , options ) ;
126
155
circular = dereferenced . circular ;
127
156
dereferencedValue = dereferenced . value ;
128
157
}
@@ -138,10 +167,18 @@ function dereference$Ref ($ref, path, pathFromRoot, parents, $refs, options) {
138
167
dereferencedValue . $ref = pathFromRoot ;
139
168
}
140
169
141
- return {
170
+
171
+ const dereferencedObject = {
142
172
circular,
143
173
value : dereferencedValue
144
174
} ;
175
+
176
+ // only cache if no extra properties than $ref
177
+ if ( Object . keys ( $ref ) . length === 1 ) {
178
+ dereferencedCache [ $refPath ] = dereferencedObject ;
179
+ }
180
+
181
+ return dereferencedObject ;
145
182
}
146
183
147
184
/**
0 commit comments