@@ -220,26 +220,46 @@ function dereference$Ref<S extends object = JSONSchema, O extends ParserOptions<
220
220
//
221
221
// If the cached object however is _not_ circular and there are additional keys alongside our
222
222
// `$ref` pointer here we should merge them back in and return that.
223
- if ( cache . circular ) {
223
+ if ( ! cache . circular ) {
224
+ const refKeys = Object . keys ( $ref ) ;
225
+ if ( refKeys . length > 1 ) {
226
+ const extraKeys = { } ;
227
+ for ( const key of refKeys ) {
228
+ if ( key !== "$ref" && ! ( key in cache . value ) ) {
229
+ // @ts -expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
230
+ extraKeys [ key ] = $ref [ key ] ;
231
+ }
232
+ }
233
+ return {
234
+ circular : cache . circular ,
235
+ value : Object . assign ( { } , cache . value , extraKeys ) ,
236
+ } ;
237
+ }
238
+
224
239
return cache ;
225
240
}
226
241
227
- const refKeys = Object . keys ( $ref ) ;
228
- if ( refKeys . length > 1 ) {
229
- const extraKeys = { } ;
230
- for ( const key of refKeys ) {
231
- if ( key !== "$ref" && ! ( key in cache . value ) ) {
232
- // @ts -expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
233
- extraKeys [ key ] = $ref [ key ] ;
234
- }
242
+ // If both our cached value and our incoming `$ref` are the same then we can return what we
243
+ // got out of the cache, otherwise we should re-process this value. We need to do this because
244
+ // the current dereference caching mechanism doesn't take into account that `$ref` are neither
245
+ // unique or reference the same file.
246
+ //
247
+ // For example if `schema.yaml` references `definitions/child.yaml` and
248
+ // `definitions/parent.yaml` references `child.yaml` then `$ref: 'child.yaml'` may get cached
249
+ // for `definitions/child.yaml`, resulting in `schema.yaml` being having an invalid reference
250
+ // to `child.yaml`.
251
+ //
252
+ // This check is not perfect and the design of the dereference caching mechanism needs a total
253
+ // overhaul.
254
+ if ( typeof cache . value === 'object' && '$ref' in cache . value && '$ref' in $ref ) {
255
+ if ( cache . value . $ref === $ref . $ref ) {
256
+ return cache ;
257
+ } else {
258
+ // no-op
235
259
}
236
- return {
237
- circular : cache . circular ,
238
- value : Object . assign ( { } , cache . value , extraKeys ) ,
239
- } ;
260
+ } else {
261
+ return cache ;
240
262
}
241
-
242
- return cache ;
243
263
}
244
264
245
265
const pointer = $refs . _resolve ( $refPath , path , options ) ;
0 commit comments