vscode-json-languageservice 5.6.3 → 5.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -364,9 +364,10 @@ function validate(n, schema, validationResult, matchingSchemas, context) {
364
364
  }
365
365
  const testAlternatives = (alternatives, maxOneMatch) => {
366
366
  const matches = [];
367
+ const alternativesToTest = _tryDiscriminatorOptimization(alternatives) ?? alternatives;
367
368
  // remember the best match that is used for error messages
368
369
  let bestMatch = undefined;
369
- for (const subSchemaRef of alternatives) {
370
+ for (const subSchemaRef of alternativesToTest) {
370
371
  const subSchema = asSchema(subSchemaRef);
371
372
  const subValidationResult = new ValidationResult();
372
373
  const subMatchingSchemas = matchingSchemas.newSub();
@@ -491,6 +492,70 @@ function validate(n, schema, validationResult, matchingSchemas, context) {
491
492
  });
492
493
  }
493
494
  }
495
+ function _tryDiscriminatorOptimization(alternatives) {
496
+ if (alternatives.length < 2) {
497
+ return undefined;
498
+ }
499
+ const buildConstMap = (getSchemas) => {
500
+ const constMap = new Map();
501
+ for (let i = 0; i < alternatives.length; i++) {
502
+ const schemas = getSchemas(asSchema(alternatives[i]), i);
503
+ if (!schemas) {
504
+ return undefined; // Early exit if any alternative can't be processed
505
+ }
506
+ schemas.forEach(([key, schema]) => {
507
+ if (schema.const !== undefined) {
508
+ if (!constMap.has(key)) {
509
+ constMap.set(key, new Map());
510
+ }
511
+ const valueMap = constMap.get(key);
512
+ if (!valueMap.has(schema.const)) {
513
+ valueMap.set(schema.const, []);
514
+ }
515
+ valueMap.get(schema.const).push(i);
516
+ }
517
+ });
518
+ }
519
+ return constMap;
520
+ };
521
+ const findDiscriminator = (constMap, getValue) => {
522
+ for (const [key, valueMap] of constMap) {
523
+ const coveredAlts = new Set();
524
+ valueMap.forEach(indices => indices.forEach(idx => coveredAlts.add(idx)));
525
+ if (coveredAlts.size === alternatives.length) {
526
+ const discriminatorValue = getValue(key);
527
+ const matchingIndices = valueMap.get(discriminatorValue);
528
+ if (matchingIndices?.length) {
529
+ return matchingIndices.map(idx => alternatives[idx]);
530
+ }
531
+ break; // Found valid discriminator but no match
532
+ }
533
+ }
534
+ return undefined;
535
+ };
536
+ if (node.type === 'object' && node.properties?.length) {
537
+ const constMap = buildConstMap((schema) => schema.properties ? Object.entries(schema.properties).map(([k, v]) => [k, asSchema(v)]) : undefined);
538
+ if (constMap) {
539
+ return findDiscriminator(constMap, (propName) => {
540
+ const prop = node.properties.find(p => p.keyNode.value === propName);
541
+ return prop?.valueNode?.type === 'string' ? prop.valueNode.value : undefined;
542
+ });
543
+ }
544
+ }
545
+ else if (node.type === 'array' && node.items?.length) {
546
+ const constMap = buildConstMap((schema) => {
547
+ const itemSchemas = schema.prefixItems || (Array.isArray(schema.items) ? schema.items : undefined);
548
+ return itemSchemas ? itemSchemas.map((item, idx) => [idx, asSchema(item)]) : undefined;
549
+ });
550
+ if (constMap) {
551
+ return findDiscriminator(constMap, (itemIndex) => {
552
+ const item = node.items[itemIndex];
553
+ return item?.type === 'string' ? item.value : undefined;
554
+ });
555
+ }
556
+ }
557
+ return undefined;
558
+ }
494
559
  function _validateNumberNode(node) {
495
560
  const val = node.value;
496
561
  function normalizeFloats(float) {
@@ -952,7 +1017,7 @@ function validate(n, schema, validationResult, matchingSchemas, context) {
952
1017
  for (const f of node.properties) {
953
1018
  const key = f.keyNode;
954
1019
  if (key) {
955
- validate(key, propertyNames, validationResult, NoOpSchemaCollector.instance, context);
1020
+ validate(key, propertyNames, validationResult, matchingSchemas, context);
956
1021
  }
957
1022
  }
958
1023
  }
@@ -394,9 +394,10 @@
394
394
  }
395
395
  const testAlternatives = (alternatives, maxOneMatch) => {
396
396
  const matches = [];
397
+ const alternativesToTest = _tryDiscriminatorOptimization(alternatives) ?? alternatives;
397
398
  // remember the best match that is used for error messages
398
399
  let bestMatch = undefined;
399
- for (const subSchemaRef of alternatives) {
400
+ for (const subSchemaRef of alternativesToTest) {
400
401
  const subSchema = asSchema(subSchemaRef);
401
402
  const subValidationResult = new ValidationResult();
402
403
  const subMatchingSchemas = matchingSchemas.newSub();
@@ -521,6 +522,70 @@
521
522
  });
522
523
  }
523
524
  }
525
+ function _tryDiscriminatorOptimization(alternatives) {
526
+ if (alternatives.length < 2) {
527
+ return undefined;
528
+ }
529
+ const buildConstMap = (getSchemas) => {
530
+ const constMap = new Map();
531
+ for (let i = 0; i < alternatives.length; i++) {
532
+ const schemas = getSchemas(asSchema(alternatives[i]), i);
533
+ if (!schemas) {
534
+ return undefined; // Early exit if any alternative can't be processed
535
+ }
536
+ schemas.forEach(([key, schema]) => {
537
+ if (schema.const !== undefined) {
538
+ if (!constMap.has(key)) {
539
+ constMap.set(key, new Map());
540
+ }
541
+ const valueMap = constMap.get(key);
542
+ if (!valueMap.has(schema.const)) {
543
+ valueMap.set(schema.const, []);
544
+ }
545
+ valueMap.get(schema.const).push(i);
546
+ }
547
+ });
548
+ }
549
+ return constMap;
550
+ };
551
+ const findDiscriminator = (constMap, getValue) => {
552
+ for (const [key, valueMap] of constMap) {
553
+ const coveredAlts = new Set();
554
+ valueMap.forEach(indices => indices.forEach(idx => coveredAlts.add(idx)));
555
+ if (coveredAlts.size === alternatives.length) {
556
+ const discriminatorValue = getValue(key);
557
+ const matchingIndices = valueMap.get(discriminatorValue);
558
+ if (matchingIndices?.length) {
559
+ return matchingIndices.map(idx => alternatives[idx]);
560
+ }
561
+ break; // Found valid discriminator but no match
562
+ }
563
+ }
564
+ return undefined;
565
+ };
566
+ if (node.type === 'object' && node.properties?.length) {
567
+ const constMap = buildConstMap((schema) => schema.properties ? Object.entries(schema.properties).map(([k, v]) => [k, asSchema(v)]) : undefined);
568
+ if (constMap) {
569
+ return findDiscriminator(constMap, (propName) => {
570
+ const prop = node.properties.find(p => p.keyNode.value === propName);
571
+ return prop?.valueNode?.type === 'string' ? prop.valueNode.value : undefined;
572
+ });
573
+ }
574
+ }
575
+ else if (node.type === 'array' && node.items?.length) {
576
+ const constMap = buildConstMap((schema) => {
577
+ const itemSchemas = schema.prefixItems || (Array.isArray(schema.items) ? schema.items : undefined);
578
+ return itemSchemas ? itemSchemas.map((item, idx) => [idx, asSchema(item)]) : undefined;
579
+ });
580
+ if (constMap) {
581
+ return findDiscriminator(constMap, (itemIndex) => {
582
+ const item = node.items[itemIndex];
583
+ return item?.type === 'string' ? item.value : undefined;
584
+ });
585
+ }
586
+ }
587
+ return undefined;
588
+ }
524
589
  function _validateNumberNode(node) {
525
590
  const val = node.value;
526
591
  function normalizeFloats(float) {
@@ -982,7 +1047,7 @@
982
1047
  for (const f of node.properties) {
983
1048
  const key = f.keyNode;
984
1049
  if (key) {
985
- validate(key, propertyNames, validationResult, NoOpSchemaCollector.instance, context);
1050
+ validate(key, propertyNames, validationResult, matchingSchemas, context);
986
1051
  }
987
1052
  }
988
1053
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vscode-json-languageservice",
3
- "version": "5.6.3",
3
+ "version": "5.6.4",
4
4
  "description": "Language service for JSON",
5
5
  "main": "./lib/umd/jsonLanguageService.js",
6
6
  "typings": "./lib/umd/jsonLanguageService",
@@ -17,9 +17,9 @@
17
17
  "devDependencies": {
18
18
  "@types/mocha": "^10.0.10",
19
19
  "@types/node": "22.x",
20
- "@typescript-eslint/eslint-plugin": "^8.46.3",
21
- "@typescript-eslint/parser": "^8.46.3",
22
- "eslint": "^9.39.0",
20
+ "@typescript-eslint/eslint-plugin": "^8.48.0",
21
+ "@typescript-eslint/parser": "^8.48.0",
22
+ "eslint": "^9.39.1",
23
23
  "json-schema-test-suite": "https://github.com/json-schema-org/JSON-Schema-Test-Suite.git#69acf52990b004240839ae19b4bec8fb01d50876",
24
24
  "mocha": "^11.7.4",
25
25
  "rimraf": "^6.1.0",