@@ -346,6 +346,22 @@ export class YAMLSchemaService extends JSONSchemaService {
346346 return this . normalizeId ( ref ) ;
347347 } ;
348348
349+ const _preferLocalBaseForRemoteId = async ( currentBase : string , id : string ) : Promise < string > => {
350+ try {
351+ const currentBaseUri = URI . parse ( currentBase ) ;
352+ const idUri = URI . parse ( id ) ;
353+ const localFileName = path . posix . basename ( idUri . path ) ;
354+ const localDir = path . posix . dirname ( currentBaseUri . path ) ;
355+ const localPath = path . posix . join ( localDir , localFileName ) ;
356+ const localUriStr = currentBaseUri . with ( { path : localPath , query : idUri . query , fragment : idUri . fragment } ) . toString ( ) ;
357+ if ( localUriStr === currentBase ) return localUriStr ;
358+ const content = await this . requestService ( localUriStr ) ;
359+ return content ? localUriStr : _resolveAgainstBase ( currentBase , id ) ;
360+ } catch {
361+ return _resolveAgainstBase ( currentBase , id ) ;
362+ }
363+ } ;
364+
349365 const _indexSchemaResources = async ( root : JSONSchema , initialBaseUri : string ) : Promise < void > => {
350366 type WorkItem = { node : JSONSchema ; baseUri : string } ;
351367 const preOrderStack : WorkItem [ ] = [ { node : root , baseUri : initialBaseUri } ] ;
@@ -364,17 +380,17 @@ export class YAMLSchemaService extends JSONSchemaService {
364380 let baseUri = current . baseUri ;
365381 const id = node . $id || node . id ;
366382 if ( id ) {
367- const normalizedId = _resolveAgainstBase ( baseUri , id ) ;
368- node . _baseUrl = normalizedId ;
369- const hashIndex = normalizedId . indexOf ( '#' ) ;
370- if ( hashIndex !== - 1 && hashIndex < normalizedId . length - 1 ) {
383+ const preferredBaseUri = await _preferLocalBaseForRemoteId ( baseUri , id ) ;
384+ node . _baseUrl = preferredBaseUri ;
385+ const hashIndex = preferredBaseUri . indexOf ( '#' ) ;
386+ if ( hashIndex !== - 1 && hashIndex < preferredBaseUri . length - 1 ) {
371387 // Draft-07 and earlier: $id with fragment defines a plain-name anchor scoped to the resolved base
372- const frag = normalizedId . slice ( hashIndex + 1 ) ;
388+ const frag = preferredBaseUri . slice ( hashIndex + 1 ) ;
373389 _getResourceIndex ( baseUri ) . fragments . set ( frag , { node } ) ;
374390 } else {
375391 // $id without fragment creates a new embedded resource scope
376- baseUri = normalizedId ;
377- const entry = _getResourceIndex ( normalizedId ) ;
392+ baseUri = preferredBaseUri ;
393+ const entry = _getResourceIndex ( preferredBaseUri ) ;
378394 if ( ! entry . root ) {
379395 entry . root = node ;
380396 }
@@ -440,7 +456,8 @@ export class YAMLSchemaService extends JSONSchemaService {
440456 } ;
441457
442458 let schema = raw as JSONSchema ;
443- await _indexSchemaResources ( schema , schemaURL ) ;
459+ const schemaBaseURL = schemaToResolve . uri ?? schemaURL ;
460+ await _indexSchemaResources ( schema , schemaBaseURL ) ;
444461
445462 const _findSection = ( schemaRoot : JSONSchema , refPath : string , sourceURI : string ) : JSONSchema => {
446463 if ( ! refPath ) {
@@ -482,6 +499,34 @@ export class YAMLSchemaService extends JSONSchemaService {
482499 }
483500 } ;
484501
502+ const _resolveRefUri = ( parentSchemaURL : string , refUri : string ) : string => {
503+ const resolvedAgainstParent = _resolveAgainstBase ( parentSchemaURL , refUri ) ;
504+ if ( ! refUri . startsWith ( '/' ) ) return resolvedAgainstParent ;
505+ const parentResource = resourceIndexByUri . get ( parentSchemaURL ) ?. root ;
506+ const parentResourceId = parentResource ?. $id || parentResource ?. id ;
507+ const resolvedParentId = _resolveAgainstBase ( parentSchemaURL , parentResourceId ) ;
508+ if ( ! resolvedParentId . startsWith ( 'http://' ) && ! resolvedParentId . startsWith ( 'https://' ) ) return resolvedAgainstParent ;
509+
510+ return _resolveAgainstBase ( resolvedParentId , refUri ) ;
511+ } ;
512+
513+ const _resolveLocalSiblingFromRemoteUri = ( parentSchemaURL : string , resolvedRefUri : string ) : string | undefined => {
514+ try {
515+ const parentUri = URI . parse ( parentSchemaURL ) ;
516+ const targetUri = URI . parse ( resolvedRefUri ) ;
517+ if ( parentUri . scheme !== 'file' ) return undefined ;
518+ if ( targetUri . scheme !== 'http' && targetUri . scheme !== 'https' ) return undefined ;
519+
520+ const localFileName = path . posix . basename ( targetUri . path ) ;
521+ if ( ! localFileName ) return undefined ;
522+ const localDir = path . posix . dirname ( parentUri . path ) ;
523+ const localPath = path . posix . join ( localDir , localFileName ) ;
524+ return parentUri . with ( { path : localPath , query : targetUri . query , fragment : targetUri . fragment } ) . toString ( ) ;
525+ } catch {
526+ return undefined ;
527+ }
528+ } ;
529+
485530 const resolveExternalLink = (
486531 node : JSONSchema ,
487532 uri : string ,
@@ -524,42 +569,57 @@ export class YAMLSchemaService extends JSONSchemaService {
524569 ) ;
525570 } ;
526571
527- const resolvedUri = _resolveAgainstBase ( parentSchemaURL , uri ) ;
528- const embeddedSchema = resourceIndexByUri . get ( resolvedUri ) ?. root ;
529- if ( embeddedSchema ) {
530- return _attachResolvedSchema (
531- node ,
532- embeddedSchema ,
533- resolvedUri ,
534- linkPath ,
535- parentSchemaDependencies ,
536- parentSchemaDependencies ,
537- resolutionStack ,
538- recursiveAnchorBase ,
539- inheritedDynamicScope
540- ) ;
541- }
572+ const _resolveByUri = ( targetUris : string [ ] , index = 0 ) : Promise < unknown > => {
573+ const targetUri = targetUris [ index ] ;
542574
543- const referencedHandle = this . getOrAddSchemaHandle ( resolvedUri ) ;
544- return referencedHandle . getUnresolvedSchema ( ) . then ( async ( unresolvedSchema ) => {
545- if ( unresolvedSchema . errors . length ) {
546- const loc = linkPath ? resolvedUri + '#' + linkPath : resolvedUri ;
547- resolveErrors . push ( l10n . t ( "Problems loading reference '{0}': {1}" , loc , unresolvedSchema . errors [ 0 ] ) ) ;
575+ const embeddedSchema = resourceIndexByUri . get ( targetUri ) ?. root ;
576+ if ( embeddedSchema ) {
577+ return _attachResolvedSchema (
578+ node ,
579+ embeddedSchema ,
580+ targetUri ,
581+ linkPath ,
582+ parentSchemaDependencies ,
583+ parentSchemaDependencies ,
584+ resolutionStack ,
585+ recursiveAnchorBase ,
586+ inheritedDynamicScope
587+ ) ;
548588 }
549- // index resources for the newly loaded schema
550- await _indexSchemaResources ( unresolvedSchema . schema , resolvedUri ) ;
551- return _attachResolvedSchema (
552- node ,
553- unresolvedSchema . schema ,
554- resolvedUri ,
555- linkPath ,
556- parentSchemaDependencies ,
557- referencedHandle . dependencies ,
558- resolutionStack ,
559- recursiveAnchorBase ,
560- inheritedDynamicScope
561- ) ;
562- } ) ;
589+
590+ const referencedHandle = this . getOrAddSchemaHandle ( targetUri ) ;
591+ return referencedHandle . getUnresolvedSchema ( ) . then ( async ( unresolvedSchema ) => {
592+ if (
593+ unresolvedSchema . errors ?. some ( ( error ) => error . toLowerCase ( ) . includes ( 'unable to load schema from' ) ) &&
594+ index + 1 < targetUris . length
595+ ) {
596+ return _resolveByUri ( targetUris , index + 1 ) ;
597+ }
598+
599+ if ( unresolvedSchema . errors . length ) {
600+ const loc = linkPath ? targetUri + '#' + linkPath : targetUri ;
601+ resolveErrors . push ( l10n . t ( "Problems loading reference '{0}': {1}" , loc , unresolvedSchema . errors [ 0 ] ) ) ;
602+ }
603+ // index resources for the newly loaded schema
604+ await _indexSchemaResources ( unresolvedSchema . schema , targetUri ) ;
605+ return _attachResolvedSchema (
606+ node ,
607+ unresolvedSchema . schema ,
608+ targetUri ,
609+ linkPath ,
610+ parentSchemaDependencies ,
611+ referencedHandle . dependencies ,
612+ resolutionStack ,
613+ recursiveAnchorBase ,
614+ inheritedDynamicScope
615+ ) ;
616+ } ) ;
617+ } ;
618+
619+ const resolvedUri = _resolveRefUri ( parentSchemaURL , uri ) ;
620+ const localSiblingUri = _resolveLocalSiblingFromRemoteUri ( parentSchemaURL , resolvedUri ) ;
621+ const targetUris = localSiblingUri && localSiblingUri !== resolvedUri ? [ localSiblingUri , resolvedUri ] : [ resolvedUri ] ;
622+ return _resolveByUri ( targetUris ) ;
563623 } ;
564624
565625 const resolveRefs = async (
0 commit comments