@@ -16,59 +16,42 @@ module.exports = dereference;
1616 * @param {$RefParserOptions } options
1717 */
1818function dereference ( parser , options ) {
19- util . debug ( 'Dereferencing $ref pointers in %s' , parser . _basePath ) ;
19+ util . debug ( 'Dereferencing $ref pointers in %s' , parser . $refs . _basePath ) ;
2020 parser . $refs . circular = false ;
21- crawl ( parser . schema , parser . _basePath , [ ] , parser . $refs , options ) ;
21+ crawl ( parser . schema , parser . $refs . _basePath , '#' , [ ] , parser . $refs , options ) ;
2222}
2323
2424/**
2525 * Recursively crawls the given value, and dereferences any JSON references.
2626 *
2727 * @param {* } obj - The value to crawl. If it's not an object or array, it will be ignored.
28- * @param {string } path - The path to use for resolving relative JSON references
28+ * @param {string } path - The full path of `obj`, possibly with a JSON Pointer in the hash
29+ * @param {string } pathFromRoot - The path of `obj` from the schema root
2930 * @param {object[] } parents - An array of the parent objects that have already been dereferenced
30- * @param {$Refs } $refs - The resolved JSON references
31+ * @param {$Refs } $refs
3132 * @param {$RefParserOptions } options
3233 * @returns {boolean } - Returns true if a circular reference was found
3334 */
34- function crawl ( obj , path , parents , $refs , options ) {
35+ function crawl ( obj , path , pathFromRoot , parents , $refs , options ) {
3536 var isCircular = false ;
3637
3738 if ( obj && typeof obj === 'object' ) {
3839 parents . push ( obj ) ;
3940
4041 Object . keys ( obj ) . forEach ( function ( key ) {
4142 var keyPath = Pointer . join ( path , key ) ;
43+ var keyPathFromRoot = Pointer . join ( pathFromRoot , key ) ;
4244 var value = obj [ key ] ;
4345 var circular = false ;
4446
4547 if ( $Ref . isAllowed$Ref ( value , options ) ) {
46- // We found a $ref, so resolve it
47- util . debug ( 'Dereferencing $ref pointer "%s" at %s' , value . $ref , keyPath ) ;
48- var $refPath = url . resolve ( path , value . $ref ) ;
49- var pointer = $refs . _resolve ( $refPath , options ) ;
50-
51- // Check for circular references
52- circular = pointer . circular || parents . indexOf ( pointer . value ) !== - 1 ;
53- circular && foundCircularReference ( keyPath , $refs , options ) ;
54-
55- // Dereference the JSON reference
56- var dereferencedValue = getDereferencedValue ( value , pointer . value ) ;
57-
58- // Crawl the dereferenced value (unless it's circular)
59- if ( ! circular ) {
60- // If the `crawl` method returns true, then dereferenced value is circular
61- circular = crawl ( dereferencedValue , pointer . path , parents , $refs , options ) ;
62- }
63-
64- // Replace the JSON reference with the dereferenced value
65- if ( ! circular || options . $refs . circular === true ) {
66- obj [ key ] = dereferencedValue ;
67- }
48+ var dereferenced = dereference$Ref ( value , keyPath , keyPathFromRoot , parents , $refs , options ) ;
49+ circular = dereferenced . circular ;
50+ obj [ key ] = dereferenced . value ;
6851 }
6952 else {
7053 if ( parents . indexOf ( value ) === - 1 ) {
71- circular = crawl ( value , keyPath , parents , $refs , options ) ;
54+ circular = crawl ( value , keyPath , keyPathFromRoot , parents , $refs , options ) ;
7255 }
7356 else {
7457 circular = foundCircularReference ( keyPath , $refs , options ) ;
@@ -85,33 +68,51 @@ function crawl(obj, path, parents, $refs, options) {
8568}
8669
8770/**
88- * Returns the dereferenced value of the given JSON reference .
71+ * Dereferences the given JSON Reference, and then crawls the resulting value .
8972 *
90- * @param {object } currentValue - the current value, which contains a JSON reference ("$ref" property)
91- * @param {* } resolvedValue - the resolved value, which can be any type
92- * @returns {* } - Returns the dereferenced value
73+ * @param {{$ref: string} } $ref - The JSON Reference to resolve
74+ * @param {string } path - The full path of `$ref`, possibly with a JSON Pointer in the hash
75+ * @param {string } pathFromRoot - The path of `$ref` from the schema root
76+ * @param {object[] } parents - An array of the parent objects that have already been dereferenced
77+ * @param {$Refs } $refs
78+ * @param {$RefParserOptions } options
79+ * @returns {object }
9380 */
94- function getDereferencedValue ( currentValue , resolvedValue ) {
95- if ( resolvedValue && typeof resolvedValue === 'object' && Object . keys ( currentValue ) . length > 1 ) {
96- // The current value has additional properties (other than "$ref"),
97- // so merge the resolved value rather than completely replacing the reference
98- var merged = { } ;
99- Object . keys ( currentValue ) . forEach ( function ( key ) {
100- if ( key !== '$ref' ) {
101- merged [ key ] = currentValue [ key ] ;
102- }
103- } ) ;
104- Object . keys ( resolvedValue ) . forEach ( function ( key ) {
105- if ( ! ( key in merged ) ) {
106- merged [ key ] = resolvedValue [ key ] ;
107- }
108- } ) ;
109- return merged ;
81+ function dereference$Ref ( $ref , path , pathFromRoot , parents , $refs , options ) {
82+ util . debug ( 'Dereferencing $ref pointer "%s" at %s' , $ref . $ref , path ) ;
83+
84+ var $refPath = url . resolve ( path , $ref . $ref ) ;
85+ var pointer = $refs . _resolve ( $refPath , options ) ;
86+
87+ // Check for circular references
88+ var directCircular = pointer . circular ;
89+ var circular = directCircular || parents . indexOf ( pointer . value ) !== - 1 ;
90+ circular && foundCircularReference ( path , $refs , options ) ;
91+
92+ // Dereference the JSON reference
93+ var dereferencedValue = util . dereference ( $ref , pointer . value ) ;
94+
95+ // Crawl the dereferenced value (unless it's circular)
96+ if ( ! circular ) {
97+ // If the `crawl` method returns true, then dereferenced value is circular
98+ circular = crawl ( dereferencedValue , pointer . path , pathFromRoot , parents , $refs , options ) ;
11099 }
111- else {
112- // Completely replace the original reference with the resolved value
113- return resolvedValue ;
100+
101+ if ( circular && ! directCircular && options . $refs . circular === 'ignore' ) {
102+ // The user has chosen to "ignore" circular references, so don't change the value
103+ dereferencedValue = $ref ;
104+ }
105+
106+ if ( directCircular ) {
107+ // The pointer is a DIRECT circular reference (i.e. it references itself).
108+ // So replace the $ref path with the absolute path from the JSON Schema root
109+ dereferencedValue . $ref = pathFromRoot ;
114110 }
111+
112+ return {
113+ circular : circular ,
114+ value : dereferencedValue
115+ } ;
115116}
116117
117118/**
0 commit comments