diff --git a/package-lock.json b/package-lock.json index 5039c87b..b07d85d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "ajv-draft-04": "^1.0.0", "prettier": "^3.8.1", "request-light": "^0.5.7", - "vscode-json-languageservice": "4.1.8", + "vscode-json-languageservice": "5.7.2", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", @@ -6400,19 +6400,16 @@ "license": "MIT" }, "node_modules/vscode-json-languageservice": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", - "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.7.2.tgz", + "integrity": "sha512-WtKRDtJfFEmLrgtu+ODexOHm/6/krRF0k6t+uvkKIKW1Jh9ZIyxZQwJJwB3qhrEgvAxa37zbUg+vn+UyUK/U2w==", "license": "MIT", "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.16.0", - "vscode-nls": "^5.0.0", - "vscode-uri": "^3.0.2" - }, - "engines": { - "npm": ">=7.0.0" + "@vscode/l10n": "^0.0.18", + "jsonc-parser": "^3.3.1", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { @@ -6458,12 +6455,6 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", "license": "MIT" }, - "node_modules/vscode-nls": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", - "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", - "license": "MIT" - }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", diff --git a/package.json b/package.json index 99c9167e..f4e32c45 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "ajv-draft-04": "^1.0.0", "prettier": "^3.8.1", "request-light": "^0.5.7", - "vscode-json-languageservice": "4.1.8", + "vscode-json-languageservice": "5.7.2", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", diff --git a/src/languageservice/services/yamlSchemaService.ts b/src/languageservice/services/yamlSchemaService.ts index ce3b9a29..dabd17b5 100644 --- a/src/languageservice/services/yamlSchemaService.ts +++ b/src/languageservice/services/yamlSchemaService.ts @@ -11,7 +11,6 @@ import { UnresolvedSchema, ResolvedSchema, JSONSchemaService, - SchemaDependencies, ISchemaContributions, SchemaHandle, } from 'vscode-json-languageservice/lib/umd/services/jsonSchemaService'; @@ -153,7 +152,7 @@ export class YAMLSchemaService extends JSONSchemaService { const result: JSONSchemaDescriptionExt[] = []; const schemaUris = new Set(); for (const filePattern of this.filePatternAssociations) { - const schemaUri = filePattern.uris[0]; + const schemaUri = filePattern.getURIs()[0]; if (schemaUris.has(schemaUri)) { continue; } @@ -198,12 +197,13 @@ export class YAMLSchemaService extends JSONSchemaService { return Object.values(map); } - async resolveSchemaContent( - schemaToResolve: UnresolvedSchema, - schemaURL: string, - dependencies: SchemaDependencies - ): Promise { - const resolveErrors: string[] = schemaToResolve.errors.slice(0); + async resolveSchemaContent(schemaToResolve: UnresolvedSchema, handle: SchemaHandle): Promise { + const schemaURL: string = handle.uri; + const dependencies: Set = handle.dependencies; + const resolveErrors: (string | { message: string; code: number })[] = schemaToResolve.errors.slice(0); + // Normalize errors: upstream v5.x expects { message, code } objects, but our code pushes strings + const _toDiagErrors = (errors: (string | { message: string; code: number })[]): { message: string; code: number }[] => + errors.map((e) => (typeof e === 'string' ? { message: e, code: 0x10000 } : e)); const loc = toDisplayString(schemaURL); const raw: unknown = schemaToResolve.schema; @@ -212,7 +212,7 @@ export class YAMLSchemaService extends JSONSchemaService { resolveErrors.push( l10n.t("Schema '{0}' is not valid: {1}", loc, `expected a JSON Schema object or boolean, got "${got}".`) ); - return new ResolvedSchema({}, resolveErrors); + return new ResolvedSchema({}, _toDiagErrors(resolveErrors)); } const _cloneSchema = ( @@ -532,7 +532,7 @@ export class YAMLSchemaService extends JSONSchemaService { uri: string, linkPath: string, parentSchemaURL: string, - parentSchemaDependencies: SchemaDependencies, + parentSchemaDependencies: Set, resolutionStack: Set, recursiveAnchorBase: string, inheritedDynamicScope: Map @@ -543,14 +543,14 @@ export class YAMLSchemaService extends JSONSchemaService { schemaRoot: JSONSchema, schemaUri: string, linkPath: string, - parentSchemaDependencies: SchemaDependencies, - resolveRefDependencies: SchemaDependencies, + parentSchemaDependencies: Set, + resolveRefDependencies: Set, resolutionStack: Set, recursiveAnchorBase: string, inheritedDynamicScope: Map // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise => { - parentSchemaDependencies[schemaUri] = true; + parentSchemaDependencies.add(schemaUri); _merge(node, schemaRoot, schemaUri, linkPath, !!inheritedDynamicScope || !!recursiveAnchorBase); if (!recursiveAnchorBase || !node._baseUrl) node._baseUrl = schemaUri; node.url = schemaUri; @@ -590,7 +590,10 @@ export class YAMLSchemaService extends JSONSchemaService { const referencedHandle = this.getOrAddSchemaHandle(targetUri); return referencedHandle.getUnresolvedSchema().then(async (unresolvedSchema) => { if ( - unresolvedSchema.errors?.some((error) => error.toLowerCase().includes('unable to load schema from')) && + unresolvedSchema.errors?.some((error) => { + const msg = typeof error === 'string' ? error : (error?.message ?? ''); + return msg.toLowerCase().includes('unable to load schema from'); + }) && index + 1 < targetUris.length ) { return _resolveByUri(targetUris, index + 1); @@ -598,7 +601,9 @@ export class YAMLSchemaService extends JSONSchemaService { if (unresolvedSchema.errors.length) { const loc = linkPath ? targetUri + '#' + linkPath : targetUri; - resolveErrors.push(l10n.t("Problems loading reference '{0}': {1}", loc, unresolvedSchema.errors[0])); + const firstError = unresolvedSchema.errors[0]; + const errorMsg = typeof firstError === 'string' ? firstError : (firstError?.message ?? String(firstError)); + resolveErrors.push(l10n.t("Problems loading reference '{0}': {1}", loc, errorMsg)); } // index resources for the newly loaded schema await _indexSchemaResources(unresolvedSchema.schema, targetUri); @@ -626,7 +631,7 @@ export class YAMLSchemaService extends JSONSchemaService { node: JSONSchema, parentSchema: JSONSchema, parentSchemaURL: string, - parentSchemaDependencies: SchemaDependencies, + parentSchemaDependencies: Set, resolutionStack: Set, recursiveAnchorBase?: string, inheritedDynamicScope?: Map @@ -939,7 +944,7 @@ export class YAMLSchemaService extends JSONSchemaService { const rootResource = schema._baseUrl || schemaURL; if (rootResource) resolutionStack.add(rootResource); await resolveRefs(schema, schema, schemaURL, dependencies, resolutionStack); - return new ResolvedSchema(schema, resolveErrors); + return new ResolvedSchema(schema, _toDiagErrors(resolveErrors)); } public getSchemaForResource(resource: string, doc: JSONDocument): Promise { @@ -973,7 +978,7 @@ export class YAMLSchemaService extends JSONSchemaService { const resolveSchemaForResource = (schemas: string[]): Promise => { const schemaHandle = super.createCombinedSchema(resource, schemas); return schemaHandle.getResolvedSchema().then((schema) => { - return this.finalizeResolvedSchema(schema, schemaHandle.url, doc, false); + return this.finalizeResolvedSchema(schema, schemaHandle.uri, doc, false); }); }; @@ -1038,14 +1043,11 @@ export class YAMLSchemaService extends JSONSchemaService { }) ).then( (schemas) => { - return { - errors: [], - schema: { - allOf: schemas.map((schemaObj) => { - return schemaObj.schema; - }), - }, - }; + return new ResolvedSchema({ + allOf: schemas.map((schemaObj) => { + return schemaObj.schema; + }), + }); }, () => { return resolveSchema(); @@ -1130,7 +1132,8 @@ export class YAMLSchemaService extends JSONSchemaService { private async resolveCustomSchema(schemaUri, doc): ResolvedSchema { const unresolvedSchema = await this.loadSchema(schemaUri); - const schema = await this.resolveSchemaContent(unresolvedSchema, schemaUri, []); + const schemaHandle = this.getOrAddSchemaHandle(schemaUri); + const schema = await this.resolveSchemaContent(unresolvedSchema, schemaHandle); return this.finalizeResolvedSchema(schema, schemaUri, doc, true); } @@ -1259,10 +1262,13 @@ export class YAMLSchemaService extends JSONSchemaService { return requestService(schemaUri).then( (content) => { if (!content) { + const errorDetails = unresolvedJsonSchema.errors + .map((e) => (typeof e === 'string' ? e : (e?.message ?? String(e)))) + .join(', '); const errorMessage = l10n.t( "Unable to load schema from '{0}': No content. {1}", toDisplayString(schemaUri), - unresolvedJsonSchema.errors + errorDetails ); return new UnresolvedSchema({}, [errorMessage]); } @@ -1294,7 +1300,8 @@ export class YAMLSchemaService extends JSONSchemaService { unresolvedJsonSchema.schema.description = description ?? unresolvedJsonSchema.schema.description; unresolvedJsonSchema.schema.versions = versions ?? unresolvedJsonSchema.schema.versions; } else if (unresolvedJsonSchema.errors && unresolvedJsonSchema.errors.length > 0) { - let errorMessage: string = unresolvedJsonSchema.errors[0]; + const firstError = unresolvedJsonSchema.errors[0]; + let errorMessage: string = typeof firstError === 'string' ? firstError : (firstError?.message ?? String(firstError)); if (errorMessage.toLowerCase().indexOf('load') !== -1) { errorMessage = l10n.t("Unable to load schema from '{0}': No content.", toDisplayString(schemaUri)); } else if (errorMessage.toLowerCase().indexOf('parse') !== -1) { @@ -1329,7 +1336,7 @@ export class YAMLSchemaService extends JSONSchemaService { if (name || description) { this.schemaUriToNameAndDescription.set(uri, { name, description, versions }); } - return super.registerExternalSchema(uri, filePatterns, unresolvedSchema); + return super.registerExternalSchema({ uri, fileMatch: filePatterns, schema: unresolvedSchema }); } clearExternalSchemas(): void { diff --git a/test/autoCompletion.test.ts b/test/autoCompletion.test.ts index aa138ff5..b4024b91 100644 --- a/test/autoCompletion.test.ts +++ b/test/autoCompletion.test.ts @@ -2131,7 +2131,7 @@ describe('Auto Completion Tests', () => { textDocument: testTextDocument, }); assert.strictEqual(result.items.length, 1, `Expecting 1 item in completion but found ${result.items.length}`); - assert.strictEqual(result.items[0].label, 'http://google.com'); + assert.strictEqual(result.items[0].label, 'http://google.com/'); }); it('should provide schema id completion in modeline for any line', async () => { @@ -2144,7 +2144,7 @@ describe('Auto Completion Tests', () => { textDocument: testTextDocument, }); assert.strictEqual(result.items.length, 1, `Expecting 1 item in completion but found ${result.items.length}`); - assert.strictEqual(result.items[0].label, 'http://google.com'); + assert.strictEqual(result.items[0].label, 'http://google.com/'); }); }); diff --git a/test/jsonParser.test.ts b/test/jsonParser.test.ts index 6eafda74..5c68130c 100644 --- a/test/jsonParser.test.ts +++ b/test/jsonParser.test.ts @@ -9,7 +9,7 @@ import { JSONDocument } from '../src/languageservice/parser/jsonDocument'; import { getNodeValue } from '../src/languageservice/parser/astNodeUtils'; import * as JsonSchema from './../src/languageservice/jsonSchema'; import { ASTNode, ObjectASTNode } from './../src/languageservice/jsonASTTypes'; -import { ErrorCode, getLanguageService } from 'vscode-json-languageservice'; +import { ErrorCode, getLanguageService, type JSONSchema } from 'vscode-json-languageservice'; import { Diagnostic, TextDocument, Range } from 'vscode-languageserver-types'; describe('JSON Parser', () => { @@ -1422,6 +1422,7 @@ describe('JSON Parser', () => { it('items as array', function () { const schema: JsonSchema.JSONSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', type: 'array', items: [ { @@ -1457,6 +1458,7 @@ describe('JSON Parser', () => { it('additionalItems', function () { let schema: JsonSchema.JSONSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', type: 'array', items: [ { @@ -1484,6 +1486,7 @@ describe('JSON Parser', () => { assert.strictEqual(semanticErrors.length, 1); } schema = { + $schema: 'http://json-schema.org/draft-07/schema#', type: 'array', items: [ { @@ -1927,7 +1930,7 @@ describe('JSON Parser', () => { res = await ls.doValidation(textDoc, jsonDoc, { trailingCommas: 'ignore' }); assert.strictEqual(res.length, 0); - const schema: JsonSchema.JSONSchema = { type: 'object', required: ['foo'] }; + const schema: JSONSchema = { type: 'object', required: ['foo'] }; res = await ls.doValidation(textDoc, jsonDoc, { trailingCommas: 'ignore' }, schema); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Missing property "foo".'); diff --git a/test/schemaValidation.test.ts b/test/schemaValidation.test.ts index 34e23b5a..01849a6f 100644 --- a/test/schemaValidation.test.ts +++ b/test/schemaValidation.test.ts @@ -71,6 +71,7 @@ describe('Validation Tests', () => { afterEach(() => { schemaProvider.deleteSchema(SCHEMA_ID); + telemetry.clearMessages(); }); describe('Boolean tests', () => { diff --git a/test/yamlLanguageService.test.ts b/test/yamlLanguageService.test.ts index 1806a4c4..238c9856 100644 --- a/test/yamlLanguageService.test.ts +++ b/test/yamlLanguageService.test.ts @@ -27,7 +27,7 @@ describe('getLanguageService()', () => { }); describe('minimal language service hover happy path', () => { - const schemaUri = 'my.schema.uri'; + const schemaUri = 'https://example.com/my.schema.json'; const schemaContentMap: { [uri: string]: string } = {}; let schemaRequestService: SchemaRequestService; @@ -79,7 +79,7 @@ describe('getLanguageService()', () => { assert.deepEqual(result, { contents: { kind: 'markdown', - value: "The person's first name.\n\nSource: [my.schema.uri](my.schema.uri)", + value: "The person's first name.\n\nSource: [my.schema.json](https://example.com/my.schema.json)", }, range: { start: {