vscode-json-languageservice 4.2.0-next.1 → 4.2.0-next.2
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.
- package/CHANGELOG.md +1 -0
- package/lib/esm/parser/jsonParser.js +7 -1
- package/lib/esm/services/jsonSchemaService.js +112 -18
- package/lib/esm/services/jsonValidation.js +2 -2
- package/lib/umd/parser/jsonParser.js +7 -1
- package/lib/umd/services/jsonSchemaService.js +112 -18
- package/lib/umd/services/jsonValidation.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,7 +28,10 @@ var formats = {
|
|
|
28
28
|
'date-time': { errorMessage: localize('dateTimeFormatWarning', 'String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
29
29
|
'date': { errorMessage: localize('dateFormatWarning', 'String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i },
|
|
30
30
|
'time': { errorMessage: localize('timeFormatWarning', 'String is not a RFC3339 time.'), pattern: /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
31
|
-
'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-
|
|
31
|
+
'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}))$/ },
|
|
32
|
+
'hostname': { errorMessage: localize('hostnameFormatWarning', 'String is not a hostname.'), pattern: /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i },
|
|
33
|
+
'ipv4': { errorMessage: localize('ipv4FormatWarning', 'String is not an IPv4 address.'), pattern: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ },
|
|
34
|
+
'ipv6': { errorMessage: localize('ipv6FormatWarning', 'String is not an IPv6 address.'), pattern: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i },
|
|
32
35
|
};
|
|
33
36
|
var ASTNodeImpl = /** @class */ (function () {
|
|
34
37
|
function ASTNodeImpl(parent, offset, length) {
|
|
@@ -655,6 +658,9 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
655
658
|
case 'date':
|
|
656
659
|
case 'time':
|
|
657
660
|
case 'email':
|
|
661
|
+
case 'hostname':
|
|
662
|
+
case 'ipv4':
|
|
663
|
+
case 'ipv6':
|
|
658
664
|
var format = formats[schema.format];
|
|
659
665
|
if (!node.value || !format.pattern.exec(node.value)) {
|
|
660
666
|
validationResult.problems.push({
|
|
@@ -59,6 +59,7 @@ var SchemaHandle = /** @class */ (function () {
|
|
|
59
59
|
this.service = service;
|
|
60
60
|
this.uri = uri;
|
|
61
61
|
this.dependencies = new Set();
|
|
62
|
+
this.anchors = new Map();
|
|
62
63
|
if (unresolvedSchemaContent) {
|
|
63
64
|
this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
|
|
64
65
|
}
|
|
@@ -73,7 +74,7 @@ var SchemaHandle = /** @class */ (function () {
|
|
|
73
74
|
var _this = this;
|
|
74
75
|
if (!this.resolvedSchema) {
|
|
75
76
|
this.resolvedSchema = this.getUnresolvedSchema().then(function (unresolved) {
|
|
76
|
-
return _this.service.resolveSchemaContent(unresolved, _this
|
|
77
|
+
return _this.service.resolveSchemaContent(unresolved, _this);
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
80
|
return this.resolvedSchema;
|
|
@@ -83,6 +84,7 @@ var SchemaHandle = /** @class */ (function () {
|
|
|
83
84
|
this.resolvedSchema = undefined;
|
|
84
85
|
this.unresolvedSchema = undefined;
|
|
85
86
|
this.dependencies.clear();
|
|
87
|
+
this.anchors.clear();
|
|
86
88
|
return hasChanges;
|
|
87
89
|
};
|
|
88
90
|
return SchemaHandle;
|
|
@@ -290,7 +292,7 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
290
292
|
return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
|
|
291
293
|
});
|
|
292
294
|
};
|
|
293
|
-
JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve,
|
|
295
|
+
JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve, handle) {
|
|
294
296
|
var _this = this;
|
|
295
297
|
var resolveErrors = schemaToResolve.errors.slice(0);
|
|
296
298
|
var schema = schemaToResolve.schema;
|
|
@@ -322,37 +324,85 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
322
324
|
});
|
|
323
325
|
return current;
|
|
324
326
|
};
|
|
325
|
-
var merge = function (target,
|
|
327
|
+
var merge = function (target, section) {
|
|
328
|
+
for (var key in section) {
|
|
329
|
+
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
|
330
|
+
target[key] = section[key];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var mergeByJsonPointer = function (target, sourceRoot, sourceURI, refSegment) {
|
|
326
335
|
var path = refSegment ? decodeURIComponent(refSegment) : undefined;
|
|
327
336
|
var section = findSection(sourceRoot, path);
|
|
328
337
|
if (section) {
|
|
329
|
-
|
|
330
|
-
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
|
331
|
-
target[key] = section[key];
|
|
332
|
-
}
|
|
333
|
-
}
|
|
338
|
+
merge(target, section);
|
|
334
339
|
}
|
|
335
340
|
else {
|
|
336
341
|
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in \'{1}\' can not be resolved.', path, sourceURI));
|
|
337
342
|
}
|
|
338
343
|
};
|
|
339
|
-
var
|
|
344
|
+
var isSubSchemaRef = function (refSegment) {
|
|
345
|
+
// Check if the first character is not '/' to determine whether it's a sub schema reference or a JSON Pointer
|
|
346
|
+
return !!refSegment && refSegment.charAt(0) !== '/';
|
|
347
|
+
};
|
|
348
|
+
var reconstructRefURI = function (uri, fragment, separator) {
|
|
349
|
+
if (separator === void 0) { separator = '#'; }
|
|
350
|
+
return normalizeId("" + uri + separator + fragment);
|
|
351
|
+
};
|
|
352
|
+
// To find which $refs point to which $ids we keep two maps:
|
|
353
|
+
// pendingSubSchemas '$id' we expect to encounter (if they exist)
|
|
354
|
+
// handle.anchors for the ones we have encountered
|
|
355
|
+
var pendingSubSchemas = new Map();
|
|
356
|
+
var tryMergeSubSchema = function (target, id, handle) {
|
|
357
|
+
// Get the full URI for the current schema to avoid matching schema1#hello and schema2#hello to the same
|
|
358
|
+
// reference by accident
|
|
359
|
+
var fullId = reconstructRefURI(handle.uri, id);
|
|
360
|
+
var resolved = handle.anchors.get(fullId);
|
|
361
|
+
if (resolved) {
|
|
362
|
+
merge(target, resolved);
|
|
363
|
+
return true; // return success
|
|
364
|
+
}
|
|
365
|
+
// This subschema has not been resolved yet
|
|
366
|
+
// Remember the target to merge later once resolved
|
|
367
|
+
var pending = pendingSubSchemas.get(fullId);
|
|
368
|
+
if (!pending) {
|
|
369
|
+
pending = [];
|
|
370
|
+
pendingSubSchemas.set(fullId, pending);
|
|
371
|
+
}
|
|
372
|
+
pending.push(target);
|
|
373
|
+
return false; // return failure - merge didn't occur
|
|
374
|
+
};
|
|
375
|
+
var resolveExternalLink = function (node, uri, refSegment, parentHandle) {
|
|
340
376
|
if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
|
|
341
|
-
uri = contextService.resolveRelativePath(uri,
|
|
377
|
+
uri = contextService.resolveRelativePath(uri, parentHandle.uri);
|
|
342
378
|
}
|
|
343
379
|
uri = normalizeId(uri);
|
|
344
380
|
var referencedHandle = _this.getOrAddSchemaHandle(uri);
|
|
345
381
|
return referencedHandle.getUnresolvedSchema().then(function (unresolvedSchema) {
|
|
346
|
-
|
|
382
|
+
parentHandle.dependencies.add(uri);
|
|
347
383
|
if (unresolvedSchema.errors.length) {
|
|
348
384
|
var loc = refSegment ? uri + '#' + refSegment : uri;
|
|
349
385
|
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
|
|
350
386
|
}
|
|
351
|
-
|
|
352
|
-
|
|
387
|
+
// A placeholder promise that might execute later a ref resolution for the newly resolved schema
|
|
388
|
+
var externalLinkPromise = Promise.resolve(true);
|
|
389
|
+
if (refSegment === undefined || !isSubSchemaRef(refSegment)) {
|
|
390
|
+
// This is not a sub schema, merge the regular way
|
|
391
|
+
mergeByJsonPointer(node, unresolvedSchema.schema, uri, refSegment);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// This is a reference to a subschema
|
|
395
|
+
if (!tryMergeSubSchema(node, refSegment, referencedHandle)) {
|
|
396
|
+
// We weren't able to merge currently so we'll try to resolve this schema first to obtain subschemas
|
|
397
|
+
// that could be missed
|
|
398
|
+
// to improve: it would be enough to find the nodes, no need to resolve the full schema
|
|
399
|
+
externalLinkPromise = resolveRefs(unresolvedSchema.schema, unresolvedSchema.schema, referencedHandle);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return externalLinkPromise.then(function () { return resolveRefs(node, unresolvedSchema.schema, referencedHandle); });
|
|
353
403
|
});
|
|
354
404
|
};
|
|
355
|
-
var resolveRefs = function (node, parentSchema,
|
|
405
|
+
var resolveRefs = function (node, parentSchema, parentHandle) {
|
|
356
406
|
if (!node || typeof node !== 'object') {
|
|
357
407
|
return Promise.resolve(null);
|
|
358
408
|
}
|
|
@@ -413,12 +463,20 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
413
463
|
var segments = ref.split('#', 2);
|
|
414
464
|
delete next.$ref;
|
|
415
465
|
if (segments[0].length > 0) {
|
|
416
|
-
|
|
466
|
+
// This is a reference to an external schema
|
|
467
|
+
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentHandle));
|
|
417
468
|
return;
|
|
418
469
|
}
|
|
419
470
|
else {
|
|
471
|
+
// This is a reference inside the current schema
|
|
420
472
|
if (!seenRefs.has(ref)) {
|
|
421
|
-
|
|
473
|
+
var id = segments[1];
|
|
474
|
+
if (id !== undefined && isSubSchemaRef(id)) { // A $ref to a sub-schema with an $id (i.e #hello)
|
|
475
|
+
tryMergeSubSchema(next, id, handle);
|
|
476
|
+
}
|
|
477
|
+
else { // A $ref to a JSON Pointer (i.e #/definitions/foo)
|
|
478
|
+
mergeByJsonPointer(next, parentSchema, parentHandle.uri, id); // can set next.$ref again, use seenRefs to avoid circle
|
|
479
|
+
}
|
|
422
480
|
seenRefs.add(ref);
|
|
423
481
|
}
|
|
424
482
|
}
|
|
@@ -427,17 +485,52 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
427
485
|
collectMapEntries(next.definitions, next.properties, next.patternProperties, next.dependencies);
|
|
428
486
|
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.items);
|
|
429
487
|
};
|
|
488
|
+
var handleId = function (next) {
|
|
489
|
+
// TODO figure out should loops be preventse
|
|
490
|
+
var id = next.$id || next.id;
|
|
491
|
+
if (typeof id === 'string' && id.charAt(0) === '#') {
|
|
492
|
+
delete next.$id;
|
|
493
|
+
delete next.id;
|
|
494
|
+
// Use a blank separator, as the $id already has the '#'
|
|
495
|
+
var fullId = reconstructRefURI(parentHandle.uri, id, '');
|
|
496
|
+
var resolved = parentHandle.anchors.get(fullId);
|
|
497
|
+
if (!resolved) {
|
|
498
|
+
// it's resolved now
|
|
499
|
+
parentHandle.anchors.set(fullId, next);
|
|
500
|
+
}
|
|
501
|
+
else if (resolved !== next) {
|
|
502
|
+
// Duplicate may occur in recursive $refs, but as long as they are the same object
|
|
503
|
+
// it's ok, otherwise report and error
|
|
504
|
+
resolveErrors.push(localize('json.schema.duplicateid', 'Duplicate id declaration: \'{0}\'', id));
|
|
505
|
+
}
|
|
506
|
+
// Resolve all pending requests and cleanup the queue list
|
|
507
|
+
var pending = pendingSubSchemas.get(fullId);
|
|
508
|
+
if (pending) {
|
|
509
|
+
for (var _i = 0, pending_1 = pending; _i < pending_1.length; _i++) {
|
|
510
|
+
var target = pending_1[_i];
|
|
511
|
+
merge(target, next);
|
|
512
|
+
}
|
|
513
|
+
pendingSubSchemas.delete(fullId);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
430
517
|
while (toWalk.length) {
|
|
431
518
|
var next = toWalk.pop();
|
|
432
519
|
if (seen.has(next)) {
|
|
433
520
|
continue;
|
|
434
521
|
}
|
|
435
522
|
seen.add(next);
|
|
523
|
+
handleId(next);
|
|
436
524
|
handleRef(next);
|
|
437
525
|
}
|
|
438
526
|
return _this.promise.all(openPromises);
|
|
439
527
|
};
|
|
440
|
-
return resolveRefs(schema, schema,
|
|
528
|
+
return resolveRefs(schema, schema, handle).then(function (_) {
|
|
529
|
+
for (var unresolvedSubschemaId in pendingSubSchemas) {
|
|
530
|
+
resolveErrors.push(localize('json.schema.idnotfound', 'Subschema with id \'{0}\' was not found', unresolvedSubschemaId));
|
|
531
|
+
}
|
|
532
|
+
return new ResolvedSchema(schema, resolveErrors);
|
|
533
|
+
});
|
|
441
534
|
};
|
|
442
535
|
JSONSchemaService.prototype.getSchemaFromProperty = function (resource, document) {
|
|
443
536
|
var _a, _b;
|
|
@@ -512,7 +605,8 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
512
605
|
JSONSchemaService.prototype.getMatchingSchemas = function (document, jsonDocument, schema) {
|
|
513
606
|
if (schema) {
|
|
514
607
|
var id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
|
|
515
|
-
|
|
608
|
+
var handle = this.addSchemaHandle(id, schema);
|
|
609
|
+
return handle.getResolvedSchema().then(function (resolvedSchema) {
|
|
516
610
|
return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(function (s) { return !s.inverted; });
|
|
517
611
|
});
|
|
518
612
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { UnresolvedSchema } from './jsonSchemaService';
|
|
6
5
|
import { ErrorCode, Diagnostic, DiagnosticSeverity, Range } from '../jsonLanguageTypes';
|
|
7
6
|
import * as nls from 'vscode-nls';
|
|
8
7
|
import { isBoolean } from '../utils/objects';
|
|
@@ -86,7 +85,8 @@ var JSONValidation = /** @class */ (function () {
|
|
|
86
85
|
};
|
|
87
86
|
if (schema) {
|
|
88
87
|
var id = schema.id || ('schemaservice://untitled/' + idCounter++);
|
|
89
|
-
|
|
88
|
+
var handle = this.jsonSchemaService.registerExternalSchema(id, [], schema);
|
|
89
|
+
return handle.getResolvedSchema().then(function (resolvedSchema) {
|
|
90
90
|
return getDiagnostics(resolvedSchema);
|
|
91
91
|
});
|
|
92
92
|
}
|
|
@@ -40,7 +40,10 @@ var __extends = (this && this.__extends) || (function () {
|
|
|
40
40
|
'date-time': { errorMessage: localize('dateTimeFormatWarning', 'String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
41
41
|
'date': { errorMessage: localize('dateFormatWarning', 'String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i },
|
|
42
42
|
'time': { errorMessage: localize('timeFormatWarning', 'String is not a RFC3339 time.'), pattern: /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
43
|
-
'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-
|
|
43
|
+
'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}))$/ },
|
|
44
|
+
'hostname': { errorMessage: localize('hostnameFormatWarning', 'String is not a hostname.'), pattern: /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i },
|
|
45
|
+
'ipv4': { errorMessage: localize('ipv4FormatWarning', 'String is not an IPv4 address.'), pattern: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ },
|
|
46
|
+
'ipv6': { errorMessage: localize('ipv6FormatWarning', 'String is not an IPv6 address.'), pattern: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i },
|
|
44
47
|
};
|
|
45
48
|
var ASTNodeImpl = /** @class */ (function () {
|
|
46
49
|
function ASTNodeImpl(parent, offset, length) {
|
|
@@ -672,6 +675,9 @@ var __extends = (this && this.__extends) || (function () {
|
|
|
672
675
|
case 'date':
|
|
673
676
|
case 'time':
|
|
674
677
|
case 'email':
|
|
678
|
+
case 'hostname':
|
|
679
|
+
case 'ipv4':
|
|
680
|
+
case 'ipv6':
|
|
675
681
|
var format = formats[schema.format];
|
|
676
682
|
if (!node.value || !format.pattern.exec(node.value)) {
|
|
677
683
|
validationResult.problems.push({
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
this.service = service;
|
|
72
72
|
this.uri = uri;
|
|
73
73
|
this.dependencies = new Set();
|
|
74
|
+
this.anchors = new Map();
|
|
74
75
|
if (unresolvedSchemaContent) {
|
|
75
76
|
this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
|
|
76
77
|
}
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
var _this = this;
|
|
86
87
|
if (!this.resolvedSchema) {
|
|
87
88
|
this.resolvedSchema = this.getUnresolvedSchema().then(function (unresolved) {
|
|
88
|
-
return _this.service.resolveSchemaContent(unresolved, _this
|
|
89
|
+
return _this.service.resolveSchemaContent(unresolved, _this);
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
92
|
return this.resolvedSchema;
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
this.resolvedSchema = undefined;
|
|
96
97
|
this.unresolvedSchema = undefined;
|
|
97
98
|
this.dependencies.clear();
|
|
99
|
+
this.anchors.clear();
|
|
98
100
|
return hasChanges;
|
|
99
101
|
};
|
|
100
102
|
return SchemaHandle;
|
|
@@ -302,7 +304,7 @@
|
|
|
302
304
|
return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
|
|
303
305
|
});
|
|
304
306
|
};
|
|
305
|
-
JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve,
|
|
307
|
+
JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve, handle) {
|
|
306
308
|
var _this = this;
|
|
307
309
|
var resolveErrors = schemaToResolve.errors.slice(0);
|
|
308
310
|
var schema = schemaToResolve.schema;
|
|
@@ -334,37 +336,85 @@
|
|
|
334
336
|
});
|
|
335
337
|
return current;
|
|
336
338
|
};
|
|
337
|
-
var merge = function (target,
|
|
339
|
+
var merge = function (target, section) {
|
|
340
|
+
for (var key in section) {
|
|
341
|
+
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
|
342
|
+
target[key] = section[key];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
var mergeByJsonPointer = function (target, sourceRoot, sourceURI, refSegment) {
|
|
338
347
|
var path = refSegment ? decodeURIComponent(refSegment) : undefined;
|
|
339
348
|
var section = findSection(sourceRoot, path);
|
|
340
349
|
if (section) {
|
|
341
|
-
|
|
342
|
-
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
|
343
|
-
target[key] = section[key];
|
|
344
|
-
}
|
|
345
|
-
}
|
|
350
|
+
merge(target, section);
|
|
346
351
|
}
|
|
347
352
|
else {
|
|
348
353
|
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in \'{1}\' can not be resolved.', path, sourceURI));
|
|
349
354
|
}
|
|
350
355
|
};
|
|
351
|
-
var
|
|
356
|
+
var isSubSchemaRef = function (refSegment) {
|
|
357
|
+
// Check if the first character is not '/' to determine whether it's a sub schema reference or a JSON Pointer
|
|
358
|
+
return !!refSegment && refSegment.charAt(0) !== '/';
|
|
359
|
+
};
|
|
360
|
+
var reconstructRefURI = function (uri, fragment, separator) {
|
|
361
|
+
if (separator === void 0) { separator = '#'; }
|
|
362
|
+
return normalizeId("" + uri + separator + fragment);
|
|
363
|
+
};
|
|
364
|
+
// To find which $refs point to which $ids we keep two maps:
|
|
365
|
+
// pendingSubSchemas '$id' we expect to encounter (if they exist)
|
|
366
|
+
// handle.anchors for the ones we have encountered
|
|
367
|
+
var pendingSubSchemas = new Map();
|
|
368
|
+
var tryMergeSubSchema = function (target, id, handle) {
|
|
369
|
+
// Get the full URI for the current schema to avoid matching schema1#hello and schema2#hello to the same
|
|
370
|
+
// reference by accident
|
|
371
|
+
var fullId = reconstructRefURI(handle.uri, id);
|
|
372
|
+
var resolved = handle.anchors.get(fullId);
|
|
373
|
+
if (resolved) {
|
|
374
|
+
merge(target, resolved);
|
|
375
|
+
return true; // return success
|
|
376
|
+
}
|
|
377
|
+
// This subschema has not been resolved yet
|
|
378
|
+
// Remember the target to merge later once resolved
|
|
379
|
+
var pending = pendingSubSchemas.get(fullId);
|
|
380
|
+
if (!pending) {
|
|
381
|
+
pending = [];
|
|
382
|
+
pendingSubSchemas.set(fullId, pending);
|
|
383
|
+
}
|
|
384
|
+
pending.push(target);
|
|
385
|
+
return false; // return failure - merge didn't occur
|
|
386
|
+
};
|
|
387
|
+
var resolveExternalLink = function (node, uri, refSegment, parentHandle) {
|
|
352
388
|
if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
|
|
353
|
-
uri = contextService.resolveRelativePath(uri,
|
|
389
|
+
uri = contextService.resolveRelativePath(uri, parentHandle.uri);
|
|
354
390
|
}
|
|
355
391
|
uri = normalizeId(uri);
|
|
356
392
|
var referencedHandle = _this.getOrAddSchemaHandle(uri);
|
|
357
393
|
return referencedHandle.getUnresolvedSchema().then(function (unresolvedSchema) {
|
|
358
|
-
|
|
394
|
+
parentHandle.dependencies.add(uri);
|
|
359
395
|
if (unresolvedSchema.errors.length) {
|
|
360
396
|
var loc = refSegment ? uri + '#' + refSegment : uri;
|
|
361
397
|
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
|
|
362
398
|
}
|
|
363
|
-
|
|
364
|
-
|
|
399
|
+
// A placeholder promise that might execute later a ref resolution for the newly resolved schema
|
|
400
|
+
var externalLinkPromise = Promise.resolve(true);
|
|
401
|
+
if (refSegment === undefined || !isSubSchemaRef(refSegment)) {
|
|
402
|
+
// This is not a sub schema, merge the regular way
|
|
403
|
+
mergeByJsonPointer(node, unresolvedSchema.schema, uri, refSegment);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
// This is a reference to a subschema
|
|
407
|
+
if (!tryMergeSubSchema(node, refSegment, referencedHandle)) {
|
|
408
|
+
// We weren't able to merge currently so we'll try to resolve this schema first to obtain subschemas
|
|
409
|
+
// that could be missed
|
|
410
|
+
// to improve: it would be enough to find the nodes, no need to resolve the full schema
|
|
411
|
+
externalLinkPromise = resolveRefs(unresolvedSchema.schema, unresolvedSchema.schema, referencedHandle);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return externalLinkPromise.then(function () { return resolveRefs(node, unresolvedSchema.schema, referencedHandle); });
|
|
365
415
|
});
|
|
366
416
|
};
|
|
367
|
-
var resolveRefs = function (node, parentSchema,
|
|
417
|
+
var resolveRefs = function (node, parentSchema, parentHandle) {
|
|
368
418
|
if (!node || typeof node !== 'object') {
|
|
369
419
|
return Promise.resolve(null);
|
|
370
420
|
}
|
|
@@ -425,12 +475,20 @@
|
|
|
425
475
|
var segments = ref.split('#', 2);
|
|
426
476
|
delete next.$ref;
|
|
427
477
|
if (segments[0].length > 0) {
|
|
428
|
-
|
|
478
|
+
// This is a reference to an external schema
|
|
479
|
+
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentHandle));
|
|
429
480
|
return;
|
|
430
481
|
}
|
|
431
482
|
else {
|
|
483
|
+
// This is a reference inside the current schema
|
|
432
484
|
if (!seenRefs.has(ref)) {
|
|
433
|
-
|
|
485
|
+
var id = segments[1];
|
|
486
|
+
if (id !== undefined && isSubSchemaRef(id)) { // A $ref to a sub-schema with an $id (i.e #hello)
|
|
487
|
+
tryMergeSubSchema(next, id, handle);
|
|
488
|
+
}
|
|
489
|
+
else { // A $ref to a JSON Pointer (i.e #/definitions/foo)
|
|
490
|
+
mergeByJsonPointer(next, parentSchema, parentHandle.uri, id); // can set next.$ref again, use seenRefs to avoid circle
|
|
491
|
+
}
|
|
434
492
|
seenRefs.add(ref);
|
|
435
493
|
}
|
|
436
494
|
}
|
|
@@ -439,17 +497,52 @@
|
|
|
439
497
|
collectMapEntries(next.definitions, next.properties, next.patternProperties, next.dependencies);
|
|
440
498
|
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.items);
|
|
441
499
|
};
|
|
500
|
+
var handleId = function (next) {
|
|
501
|
+
// TODO figure out should loops be preventse
|
|
502
|
+
var id = next.$id || next.id;
|
|
503
|
+
if (typeof id === 'string' && id.charAt(0) === '#') {
|
|
504
|
+
delete next.$id;
|
|
505
|
+
delete next.id;
|
|
506
|
+
// Use a blank separator, as the $id already has the '#'
|
|
507
|
+
var fullId = reconstructRefURI(parentHandle.uri, id, '');
|
|
508
|
+
var resolved = parentHandle.anchors.get(fullId);
|
|
509
|
+
if (!resolved) {
|
|
510
|
+
// it's resolved now
|
|
511
|
+
parentHandle.anchors.set(fullId, next);
|
|
512
|
+
}
|
|
513
|
+
else if (resolved !== next) {
|
|
514
|
+
// Duplicate may occur in recursive $refs, but as long as they are the same object
|
|
515
|
+
// it's ok, otherwise report and error
|
|
516
|
+
resolveErrors.push(localize('json.schema.duplicateid', 'Duplicate id declaration: \'{0}\'', id));
|
|
517
|
+
}
|
|
518
|
+
// Resolve all pending requests and cleanup the queue list
|
|
519
|
+
var pending = pendingSubSchemas.get(fullId);
|
|
520
|
+
if (pending) {
|
|
521
|
+
for (var _i = 0, pending_1 = pending; _i < pending_1.length; _i++) {
|
|
522
|
+
var target = pending_1[_i];
|
|
523
|
+
merge(target, next);
|
|
524
|
+
}
|
|
525
|
+
pendingSubSchemas.delete(fullId);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
};
|
|
442
529
|
while (toWalk.length) {
|
|
443
530
|
var next = toWalk.pop();
|
|
444
531
|
if (seen.has(next)) {
|
|
445
532
|
continue;
|
|
446
533
|
}
|
|
447
534
|
seen.add(next);
|
|
535
|
+
handleId(next);
|
|
448
536
|
handleRef(next);
|
|
449
537
|
}
|
|
450
538
|
return _this.promise.all(openPromises);
|
|
451
539
|
};
|
|
452
|
-
return resolveRefs(schema, schema,
|
|
540
|
+
return resolveRefs(schema, schema, handle).then(function (_) {
|
|
541
|
+
for (var unresolvedSubschemaId in pendingSubSchemas) {
|
|
542
|
+
resolveErrors.push(localize('json.schema.idnotfound', 'Subschema with id \'{0}\' was not found', unresolvedSubschemaId));
|
|
543
|
+
}
|
|
544
|
+
return new ResolvedSchema(schema, resolveErrors);
|
|
545
|
+
});
|
|
453
546
|
};
|
|
454
547
|
JSONSchemaService.prototype.getSchemaFromProperty = function (resource, document) {
|
|
455
548
|
var _a, _b;
|
|
@@ -524,7 +617,8 @@
|
|
|
524
617
|
JSONSchemaService.prototype.getMatchingSchemas = function (document, jsonDocument, schema) {
|
|
525
618
|
if (schema) {
|
|
526
619
|
var id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
|
|
527
|
-
|
|
620
|
+
var handle = this.addSchemaHandle(id, schema);
|
|
621
|
+
return handle.getResolvedSchema().then(function (resolvedSchema) {
|
|
528
622
|
return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(function (s) { return !s.inverted; });
|
|
529
623
|
});
|
|
530
624
|
}
|
|
@@ -8,13 +8,12 @@
|
|
|
8
8
|
if (v !== undefined) module.exports = v;
|
|
9
9
|
}
|
|
10
10
|
else if (typeof define === "function" && define.amd) {
|
|
11
|
-
define(["require", "exports", "
|
|
11
|
+
define(["require", "exports", "../jsonLanguageTypes", "vscode-nls", "../utils/objects"], factory);
|
|
12
12
|
}
|
|
13
13
|
})(function (require, exports) {
|
|
14
14
|
"use strict";
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.JSONValidation = void 0;
|
|
17
|
-
var jsonSchemaService_1 = require("./jsonSchemaService");
|
|
18
17
|
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
|
|
19
18
|
var nls = require("vscode-nls");
|
|
20
19
|
var objects_1 = require("../utils/objects");
|
|
@@ -98,7 +97,8 @@
|
|
|
98
97
|
};
|
|
99
98
|
if (schema) {
|
|
100
99
|
var id = schema.id || ('schemaservice://untitled/' + idCounter++);
|
|
101
|
-
|
|
100
|
+
var handle = this.jsonSchemaService.registerExternalSchema(id, [], schema);
|
|
101
|
+
return handle.getResolvedSchema().then(function (resolvedSchema) {
|
|
102
102
|
return getDiagnostics(resolvedSchema);
|
|
103
103
|
});
|
|
104
104
|
}
|