From 8b240d2cb95ea3d0511607c845fc89d5c81791b2 Mon Sep 17 00:00:00 2001 From: Morgan Chang Date: Mon, 13 Apr 2026 16:48:58 -0400 Subject: [PATCH 1/2] fix absolute-path $ref resolution for schemas without a top-level $id Signed-off-by: Morgan Chang --- .../services/yamlSchemaService.ts | 1 + test/schema.test.ts | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/languageservice/services/yamlSchemaService.ts b/src/languageservice/services/yamlSchemaService.ts index b6534452a..af9f7b2f3 100644 --- a/src/languageservice/services/yamlSchemaService.ts +++ b/src/languageservice/services/yamlSchemaService.ts @@ -505,6 +505,7 @@ export class YAMLSchemaService extends JSONSchemaService { if (!refUri.startsWith('/')) return resolvedAgainstParent; const parentResource = resourceIndexByUri.get(parentSchemaURL)?.root; const parentResourceId = parentResource?.$id || parentResource?.id; + if (!parentResourceId) return resolvedAgainstParent; const resolvedParentId = _resolveAgainstBase(parentSchemaURL, parentResourceId); if (!resolvedParentId.startsWith('http://') && !resolvedParentId.startsWith('https://')) return resolvedAgainstParent; diff --git a/test/schema.test.ts b/test/schema.test.ts index cc6e9b67d..f96071d4d 100644 --- a/test/schema.test.ts +++ b/test/schema.test.ts @@ -219,6 +219,47 @@ describe('JSON Schema', () => { ); }); + it('Resolving absolute-path $refs without top-level id', function (testDone) { + const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext); + service.setSchemaContributions({ + schemas: { + 'file:///main/schema.yaml': { + type: 'object', + properties: { + name: { + $ref: '/main/schema2.yaml', + }, + }, + }, + 'file:///main/schema2.yaml': { + type: 'string', + description: 'Something.', + }, + }, + }); + + service + .getResolvedSchema('file:///main/schema.yaml') + .then((fs) => { + assert.deepEqual(fs.errors, []); + assert.deepEqual(fs.schema.properties['name'], { + type: 'string', + description: 'Something.', + _$ref: '/main/schema2.yaml', + _baseUrl: 'file:///main/schema2.yaml', + url: 'file:///main/schema2.yaml', + }); + }) + .then( + () => { + return testDone(); + }, + (error) => { + testDone(error); + } + ); + }); + it('Preserves markdownDescription on $ref siblings', function (testDone) { const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext); service.setSchemaContributions({ From 43776c32e0b4e1c64efa10be4246d7c9109b570b Mon Sep 17 00:00:00 2001 From: Morgan Chang Date: Mon, 13 Apr 2026 17:01:37 -0400 Subject: [PATCH 2/2] implement automated test Signed-off-by: Morgan Chang --- test/schema.test.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/test/schema.test.ts b/test/schema.test.ts index f96071d4d..8c7d07616 100644 --- a/test/schema.test.ts +++ b/test/schema.test.ts @@ -223,31 +223,34 @@ describe('JSON Schema', () => { const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext); service.setSchemaContributions({ schemas: { - 'file:///main/schema.yaml': { + 'file:///main/schema.json': { type: 'object', properties: { name: { - $ref: '/main/schema2.yaml', + $ref: '/main/schema2.json', }, }, + required: ['name'], }, - 'file:///main/schema2.yaml': { + 'file:///main/schema2.json': { type: 'string', - description: 'Something.', + enum: ['alice', 'bob'], + description: 'Allowed names.', }, }, }); service - .getResolvedSchema('file:///main/schema.yaml') + .getResolvedSchema('file:///main/schema.json') .then((fs) => { assert.deepEqual(fs.errors, []); assert.deepEqual(fs.schema.properties['name'], { type: 'string', - description: 'Something.', - _$ref: '/main/schema2.yaml', - _baseUrl: 'file:///main/schema2.yaml', - url: 'file:///main/schema2.yaml', + enum: ['alice', 'bob'], + description: 'Allowed names.', + _$ref: '/main/schema2.json', + _baseUrl: 'file:///main/schema2.json', + url: 'file:///main/schema2.json', }); }) .then(