vscode-json-languageservice
Version:
Language service for JSON
140 lines (139 loc) • 6.74 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SchemaDraft } from '../jsonLanguageTypes.js';
const CORE_201909 = 'https://json-schema.org/draft/2019-09/vocab/core';
const CORE_202012 = 'https://json-schema.org/draft/2020-12/vocab/core';
const vocabularyKeywords = {
[CORE_201909]: [
'$id', '$schema', '$ref', '$anchor', '$recursiveRef',
'$recursiveAnchor', '$defs', '$comment', '$vocabulary'
],
'https://json-schema.org/draft/2019-09/vocab/applicator': [
'prefixItems', 'items', 'contains', 'additionalProperties',
'properties', 'patternProperties', 'dependentSchemas',
'propertyNames', 'if', 'then', 'else', 'allOf', 'anyOf', 'oneOf', 'not'
],
'https://json-schema.org/draft/2019-09/vocab/validation': [
'type', 'enum', 'const', 'multipleOf', 'maximum', 'exclusiveMaximum',
'minimum', 'exclusiveMinimum', 'maxLength', 'minLength', 'pattern',
'maxItems', 'minItems', 'uniqueItems', 'maxContains', 'minContains',
'maxProperties', 'minProperties', 'required', 'dependentRequired'
],
'https://json-schema.org/draft/2019-09/vocab/meta-data': [
'title', 'description', 'default', 'deprecated',
'readOnly', 'writeOnly', 'examples'
],
'https://json-schema.org/draft/2019-09/vocab/format': [
'format'
],
'https://json-schema.org/draft/2019-09/vocab/content': [
'contentEncoding', 'contentMediaType', 'contentSchema'
],
[CORE_202012]: [
'$id', '$schema', '$ref', '$anchor', '$dynamicRef',
'$dynamicAnchor', '$defs', '$comment', '$vocabulary'
],
'https://json-schema.org/draft/2020-12/vocab/applicator': [
'prefixItems', 'items', 'contains', 'additionalProperties',
'properties', 'patternProperties', 'dependentSchemas',
'propertyNames', 'if', 'then', 'else', 'allOf', 'anyOf', 'oneOf', 'not'
],
'https://json-schema.org/draft/2020-12/vocab/unevaluated': [
'unevaluatedItems', 'unevaluatedProperties'
],
'https://json-schema.org/draft/2020-12/vocab/validation': [
'type', 'enum', 'const', 'multipleOf', 'maximum', 'exclusiveMaximum',
'minimum', 'exclusiveMinimum', 'maxLength', 'minLength', 'pattern',
'maxItems', 'minItems', 'uniqueItems', 'maxContains', 'minContains',
'maxProperties', 'minProperties', 'required', 'dependentRequired'
],
'https://json-schema.org/draft/2020-12/vocab/meta-data': [
'title', 'description', 'default', 'deprecated',
'readOnly', 'writeOnly', 'examples'
],
'https://json-schema.org/draft/2020-12/vocab/format-annotation': [
'format'
],
'https://json-schema.org/draft/2020-12/vocab/format-assertion': [
'format'
],
'https://json-schema.org/draft/2020-12/vocab/content': [
'contentEncoding', 'contentMediaType', 'contentSchema'
]
};
/*
* Checks if a keyword is enabled based on the active vocabularies.
* If no vocabulary constraints are present, all keywords are enabled.
* Core keywords are always enabled regardless of vocabulary settings.
*
* @param keyword The keyword to check (e.g., 'type', 'properties', '$ref')
* @param activeVocabularies Set of active vocabulary URIs, or undefined if no constraints
* @returns true if the keyword should be processed, false otherwise
*/
export function isKeywordEnabled(keyword, activeVocabularies) {
// If no vocabulary constraints, treat all keywords as enabled
if (!activeVocabularies) {
return true;
}
// Check if this keyword belongs to any active vocabulary
for (const [vocabUri, keywords] of Object.entries(vocabularyKeywords)) {
if (keywords.includes(keyword) && activeVocabularies.has(vocabUri)) {
return true;
}
}
// Core keywords are always enabled per JSON Schema spec.
// Check both 2019-09 and 2020-12 core vocabularies.
if (vocabularyKeywords[CORE_201909].includes(keyword) ||
vocabularyKeywords[CORE_202012].includes(keyword)) {
return true;
}
// Keyword not found in any vocabulary - disable it
return false;
}
/*
* Checks if format validation should produce assertion errors.
*
* According to JSON Schema 2020-12:
* - format-annotation: format is purely informational, no validation errors
* - format-assertion: format must be validated and can produce errors
*
* For backwards compatibility:
* - If no vocabularies are specified and no explicit 2019-09+ draft, format asserts
* - 2019-09 format vocabulary asserts when required, annotation-only when optional
* - 2020-12 format-assertion vocabulary asserts
* - 2020-12 format-annotation vocabulary does not assert
*
* @param activeVocabularies Map of active vocabulary URIs to required flag, or undefined if no constraints
* @param schemaDraft The explicitly declared schema draft (only set when $schema was present), or undefined
* @returns true if format validation should produce errors, false if annotation-only
*/
export function isFormatAssertionEnabled(activeVocabularies, schemaDraft) {
// If vocabulary constraints are present, use them to determine format assertion
if (activeVocabularies && activeVocabularies.size > 0) {
// 2020-12 format-assertion explicitly enables format validation errors
if (activeVocabularies.has('https://json-schema.org/draft/2020-12/vocab/format-assertion')) {
return true;
}
// 2019-09 uses the format vocabulary value to control assertions:
// true enables assertion, false leaves format as annotation-only.
const format201909 = activeVocabularies.get('https://json-schema.org/draft/2019-09/vocab/format');
if (format201909 !== undefined) {
return format201909;
}
// 2020-12 format-annotation is annotation-only, no assertion
if (activeVocabularies.has('https://json-schema.org/draft/2020-12/vocab/format-annotation')) {
return false;
}
// No format vocabulary active - no assertion
return false;
}
// No vocabulary constraints:
// For explicitly declared 2019-09+ schemas, format is annotation-only by default per spec.
// For older drafts or no explicit $schema, format asserts for backward compatibility.
if (schemaDraft !== undefined && schemaDraft >= SchemaDraft.v2019_09) {
return false;
}
return true;
}