vscode-json-languageservice 4.2.0 → 5.1.0
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 +10 -1
- package/SECURITY.md +41 -0
- package/lib/esm/jsonLanguageService.js +22 -22
- package/lib/esm/jsonLanguageTypes.d.ts +3 -1
- package/lib/esm/jsonLanguageTypes.js +3 -2
- package/lib/esm/jsonSchema.d.ts +21 -2
- package/lib/esm/parser/jsonParser.js +488 -488
- package/lib/esm/services/configuration.js +9 -9
- package/lib/esm/services/jsonCompletion.js +280 -290
- package/lib/esm/services/jsonDocumentSymbols.js +88 -99
- package/lib/esm/services/jsonFolding.js +38 -39
- package/lib/esm/services/jsonHover.js +40 -43
- package/lib/esm/services/jsonLinks.js +12 -13
- package/lib/esm/services/jsonSchemaService.js +234 -253
- package/lib/esm/services/jsonSelectionRanges.js +9 -9
- package/lib/esm/services/jsonValidation.js +53 -51
- package/lib/esm/utils/colors.js +7 -8
- package/lib/esm/utils/glob.js +12 -12
- package/lib/esm/utils/json.js +7 -7
- package/lib/esm/utils/objects.js +3 -0
- package/lib/esm/utils/strings.js +3 -3
- package/lib/umd/jsonLanguageService.js +36 -32
- package/lib/umd/jsonLanguageTypes.d.ts +3 -1
- package/lib/umd/jsonLanguageTypes.js +5 -3
- package/lib/umd/jsonSchema.d.ts +21 -2
- package/lib/umd/parser/jsonParser.js +492 -482
- package/lib/umd/services/configuration.js +9 -9
- package/lib/umd/services/jsonCompletion.js +287 -296
- package/lib/umd/services/jsonDocumentSymbols.js +92 -102
- package/lib/umd/services/jsonFolding.js +40 -41
- package/lib/umd/services/jsonHover.js +42 -44
- package/lib/umd/services/jsonLinks.js +13 -14
- package/lib/umd/services/jsonSchemaService.js +241 -257
- package/lib/umd/services/jsonSelectionRanges.js +11 -11
- package/lib/umd/services/jsonValidation.js +56 -53
- package/lib/umd/utils/colors.js +7 -8
- package/lib/umd/utils/glob.js +12 -12
- package/lib/umd/utils/json.js +7 -7
- package/lib/umd/utils/objects.js +5 -1
- package/lib/umd/utils/strings.js +3 -3
- package/package.json +12 -12
|
@@ -8,16 +8,16 @@ import * as Strings from '../utils/strings';
|
|
|
8
8
|
import * as Parser from '../parser/jsonParser';
|
|
9
9
|
import * as nls from 'vscode-nls';
|
|
10
10
|
import { createRegex } from '../utils/glob';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
import { isObject, isString } from '../utils/objects';
|
|
12
|
+
const localize = nls.loadMessageBundle();
|
|
13
|
+
const BANG = '!';
|
|
14
|
+
const PATH_SEP = '/';
|
|
15
|
+
class FilePatternAssociation {
|
|
16
|
+
constructor(pattern, uris) {
|
|
16
17
|
this.globWrappers = [];
|
|
17
18
|
try {
|
|
18
|
-
for (
|
|
19
|
-
|
|
20
|
-
var include = patternString[0] !== BANG;
|
|
19
|
+
for (let patternString of pattern) {
|
|
20
|
+
const include = patternString[0] !== BANG;
|
|
21
21
|
if (!include) {
|
|
22
22
|
patternString = patternString.substring(1);
|
|
23
23
|
}
|
|
@@ -39,23 +39,21 @@ var FilePatternAssociation = /** @class */ (function () {
|
|
|
39
39
|
this.uris = [];
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
for (
|
|
45
|
-
var _b = _a[_i], regexp = _b.regexp, include = _b.include;
|
|
42
|
+
matchesPattern(fileName) {
|
|
43
|
+
let match = false;
|
|
44
|
+
for (const { regexp, include } of this.globWrappers) {
|
|
46
45
|
if (regexp.test(fileName)) {
|
|
47
46
|
match = include;
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
return match;
|
|
51
|
-
}
|
|
52
|
-
|
|
50
|
+
}
|
|
51
|
+
getURIs() {
|
|
53
52
|
return this.uris;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function SchemaHandle(service, uri, unresolvedSchemaContent) {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
class SchemaHandle {
|
|
56
|
+
constructor(service, uri, unresolvedSchemaContent) {
|
|
59
57
|
this.service = service;
|
|
60
58
|
this.uri = uri;
|
|
61
59
|
this.dependencies = new Set();
|
|
@@ -64,66 +62,61 @@ var SchemaHandle = /** @class */ (function () {
|
|
|
64
62
|
this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
|
-
|
|
65
|
+
getUnresolvedSchema() {
|
|
68
66
|
if (!this.unresolvedSchema) {
|
|
69
67
|
this.unresolvedSchema = this.service.loadSchema(this.uri);
|
|
70
68
|
}
|
|
71
69
|
return this.unresolvedSchema;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
var _this = this;
|
|
70
|
+
}
|
|
71
|
+
getResolvedSchema() {
|
|
75
72
|
if (!this.resolvedSchema) {
|
|
76
|
-
this.resolvedSchema = this.getUnresolvedSchema().then(
|
|
77
|
-
return
|
|
73
|
+
this.resolvedSchema = this.getUnresolvedSchema().then(unresolved => {
|
|
74
|
+
return this.service.resolveSchemaContent(unresolved, this);
|
|
78
75
|
});
|
|
79
76
|
}
|
|
80
77
|
return this.resolvedSchema;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
}
|
|
79
|
+
clearSchema() {
|
|
80
|
+
const hasChanges = !!this.unresolvedSchema;
|
|
84
81
|
this.resolvedSchema = undefined;
|
|
85
82
|
this.unresolvedSchema = undefined;
|
|
86
83
|
this.dependencies.clear();
|
|
87
84
|
this.anchors = undefined;
|
|
88
85
|
return hasChanges;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
function UnresolvedSchema(schema, errors) {
|
|
94
|
-
if (errors === void 0) { errors = []; }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export class UnresolvedSchema {
|
|
89
|
+
constructor(schema, errors = []) {
|
|
95
90
|
this.schema = schema;
|
|
96
91
|
this.errors = errors;
|
|
97
92
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
var ResolvedSchema = /** @class */ (function () {
|
|
102
|
-
function ResolvedSchema(schema, errors) {
|
|
103
|
-
if (errors === void 0) { errors = []; }
|
|
93
|
+
}
|
|
94
|
+
export class ResolvedSchema {
|
|
95
|
+
constructor(schema, errors = [], warnings = [], schemaDraft) {
|
|
104
96
|
this.schema = schema;
|
|
105
97
|
this.errors = errors;
|
|
98
|
+
this.warnings = warnings;
|
|
99
|
+
this.schemaDraft = schemaDraft;
|
|
106
100
|
}
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
getSection(path) {
|
|
102
|
+
const schemaRef = this.getSectionRecursive(path, this.schema);
|
|
109
103
|
if (schemaRef) {
|
|
110
104
|
return Parser.asSchema(schemaRef);
|
|
111
105
|
}
|
|
112
106
|
return undefined;
|
|
113
|
-
}
|
|
114
|
-
|
|
107
|
+
}
|
|
108
|
+
getSectionRecursive(path, schema) {
|
|
115
109
|
if (!schema || typeof schema === 'boolean' || path.length === 0) {
|
|
116
110
|
return schema;
|
|
117
111
|
}
|
|
118
|
-
|
|
112
|
+
const next = path.shift();
|
|
119
113
|
if (schema.properties && typeof schema.properties[next]) {
|
|
120
114
|
return this.getSectionRecursive(path, schema.properties[next]);
|
|
121
115
|
}
|
|
122
116
|
else if (schema.patternProperties) {
|
|
123
|
-
for (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (regex === null || regex === void 0 ? void 0 : regex.test(next)) {
|
|
117
|
+
for (const pattern of Object.keys(schema.patternProperties)) {
|
|
118
|
+
const regex = Strings.extendedRegExp(pattern);
|
|
119
|
+
if (regex?.test(next)) {
|
|
127
120
|
return this.getSectionRecursive(path, schema.patternProperties[pattern]);
|
|
128
121
|
}
|
|
129
122
|
}
|
|
@@ -133,7 +126,7 @@ var ResolvedSchema = /** @class */ (function () {
|
|
|
133
126
|
}
|
|
134
127
|
else if (next.match('[0-9]+')) {
|
|
135
128
|
if (Array.isArray(schema.items)) {
|
|
136
|
-
|
|
129
|
+
const index = parseInt(next, 10);
|
|
137
130
|
if (!isNaN(index) && schema.items[index]) {
|
|
138
131
|
return this.getSectionRecursive(path, schema.items[index]);
|
|
139
132
|
}
|
|
@@ -143,12 +136,10 @@ var ResolvedSchema = /** @class */ (function () {
|
|
|
143
136
|
}
|
|
144
137
|
}
|
|
145
138
|
return undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
var JSONSchemaService = /** @class */ (function () {
|
|
151
|
-
function JSONSchemaService(requestService, contextService, promiseConstructor) {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export class JSONSchemaService {
|
|
142
|
+
constructor(requestService, contextService, promiseConstructor) {
|
|
152
143
|
this.contextService = contextService;
|
|
153
144
|
this.requestService = requestService;
|
|
154
145
|
this.promiseConstructor = promiseConstructor || Promise;
|
|
@@ -159,36 +150,31 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
159
150
|
this.filePatternAssociations = [];
|
|
160
151
|
this.registeredSchemasIds = {};
|
|
161
152
|
}
|
|
162
|
-
|
|
163
|
-
return Object.keys(this.registeredSchemasIds).filter(
|
|
164
|
-
|
|
153
|
+
getRegisteredSchemaIds(filter) {
|
|
154
|
+
return Object.keys(this.registeredSchemasIds).filter(id => {
|
|
155
|
+
const scheme = URI.parse(id).scheme;
|
|
165
156
|
return scheme !== 'schemaservice' && (!filter || filter(scheme));
|
|
166
157
|
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
enumerable: false,
|
|
173
|
-
configurable: true
|
|
174
|
-
});
|
|
175
|
-
JSONSchemaService.prototype.dispose = function () {
|
|
158
|
+
}
|
|
159
|
+
get promise() {
|
|
160
|
+
return this.promiseConstructor;
|
|
161
|
+
}
|
|
162
|
+
dispose() {
|
|
176
163
|
while (this.callOnDispose.length > 0) {
|
|
177
164
|
this.callOnDispose.pop()();
|
|
178
165
|
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
var _this = this;
|
|
166
|
+
}
|
|
167
|
+
onResourceChange(uri) {
|
|
182
168
|
// always clear this local cache when a resource changes
|
|
183
169
|
this.cachedSchemaForResource = undefined;
|
|
184
|
-
|
|
170
|
+
let hasChanges = false;
|
|
185
171
|
uri = normalizeId(uri);
|
|
186
|
-
|
|
187
|
-
|
|
172
|
+
const toWalk = [uri];
|
|
173
|
+
const all = Object.keys(this.schemasById).map(key => this.schemasById[key]);
|
|
188
174
|
while (toWalk.length) {
|
|
189
|
-
|
|
190
|
-
for (
|
|
191
|
-
|
|
175
|
+
const curr = toWalk.pop();
|
|
176
|
+
for (let i = 0; i < all.length; i++) {
|
|
177
|
+
const handle = all[i];
|
|
192
178
|
if (handle && (handle.uri === curr || handle.dependencies.has(curr))) {
|
|
193
179
|
if (handle.uri !== curr) {
|
|
194
180
|
toWalk.push(handle.uri);
|
|
@@ -201,87 +187,85 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
201
187
|
}
|
|
202
188
|
}
|
|
203
189
|
return hasChanges;
|
|
204
|
-
}
|
|
205
|
-
|
|
190
|
+
}
|
|
191
|
+
setSchemaContributions(schemaContributions) {
|
|
206
192
|
if (schemaContributions.schemas) {
|
|
207
|
-
|
|
208
|
-
for (
|
|
209
|
-
|
|
193
|
+
const schemas = schemaContributions.schemas;
|
|
194
|
+
for (const id in schemas) {
|
|
195
|
+
const normalizedId = normalizeId(id);
|
|
210
196
|
this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
|
|
211
197
|
}
|
|
212
198
|
}
|
|
213
199
|
if (Array.isArray(schemaContributions.schemaAssociations)) {
|
|
214
|
-
|
|
215
|
-
for (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
var association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
|
|
200
|
+
const schemaAssociations = schemaContributions.schemaAssociations;
|
|
201
|
+
for (let schemaAssociation of schemaAssociations) {
|
|
202
|
+
const uris = schemaAssociation.uris.map(normalizeId);
|
|
203
|
+
const association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
|
|
219
204
|
this.contributionAssociations.push(association);
|
|
220
205
|
}
|
|
221
206
|
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
207
|
+
}
|
|
208
|
+
addSchemaHandle(id, unresolvedSchemaContent) {
|
|
209
|
+
const schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
|
|
225
210
|
this.schemasById[id] = schemaHandle;
|
|
226
211
|
return schemaHandle;
|
|
227
|
-
}
|
|
228
|
-
|
|
212
|
+
}
|
|
213
|
+
getOrAddSchemaHandle(id, unresolvedSchemaContent) {
|
|
229
214
|
return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
215
|
+
}
|
|
216
|
+
addFilePatternAssociation(pattern, uris) {
|
|
217
|
+
const fpa = new FilePatternAssociation(pattern, uris);
|
|
233
218
|
this.filePatternAssociations.push(fpa);
|
|
234
219
|
return fpa;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
220
|
+
}
|
|
221
|
+
registerExternalSchema(uri, filePatterns, unresolvedSchemaContent) {
|
|
222
|
+
const id = normalizeId(uri);
|
|
238
223
|
this.registeredSchemasIds[id] = true;
|
|
239
224
|
this.cachedSchemaForResource = undefined;
|
|
240
225
|
if (filePatterns) {
|
|
241
226
|
this.addFilePatternAssociation(filePatterns, [id]);
|
|
242
227
|
}
|
|
243
228
|
return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
|
|
244
|
-
}
|
|
245
|
-
|
|
229
|
+
}
|
|
230
|
+
clearExternalSchemas() {
|
|
246
231
|
this.schemasById = {};
|
|
247
232
|
this.filePatternAssociations = [];
|
|
248
233
|
this.registeredSchemasIds = {};
|
|
249
234
|
this.cachedSchemaForResource = undefined;
|
|
250
|
-
for (
|
|
235
|
+
for (const id in this.contributionSchemas) {
|
|
251
236
|
this.schemasById[id] = this.contributionSchemas[id];
|
|
252
237
|
this.registeredSchemasIds[id] = true;
|
|
253
238
|
}
|
|
254
|
-
for (
|
|
255
|
-
var contributionAssociation = _a[_i];
|
|
239
|
+
for (const contributionAssociation of this.contributionAssociations) {
|
|
256
240
|
this.filePatternAssociations.push(contributionAssociation);
|
|
257
241
|
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
242
|
+
}
|
|
243
|
+
getResolvedSchema(schemaId) {
|
|
244
|
+
const id = normalizeId(schemaId);
|
|
245
|
+
const schemaHandle = this.schemasById[id];
|
|
262
246
|
if (schemaHandle) {
|
|
263
247
|
return schemaHandle.getResolvedSchema();
|
|
264
248
|
}
|
|
265
249
|
return this.promise.resolve(undefined);
|
|
266
|
-
}
|
|
267
|
-
|
|
250
|
+
}
|
|
251
|
+
loadSchema(url) {
|
|
268
252
|
if (!this.requestService) {
|
|
269
|
-
|
|
253
|
+
const errorMessage = localize('json.schema.norequestservice', 'Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url));
|
|
270
254
|
return this.promise.resolve(new UnresolvedSchema({}, [errorMessage]));
|
|
271
255
|
}
|
|
272
|
-
return this.requestService(url).then(
|
|
256
|
+
return this.requestService(url).then(content => {
|
|
273
257
|
if (!content) {
|
|
274
|
-
|
|
258
|
+
const errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
|
|
275
259
|
return new UnresolvedSchema({}, [errorMessage]);
|
|
276
260
|
}
|
|
277
|
-
|
|
278
|
-
|
|
261
|
+
let schemaContent = {};
|
|
262
|
+
const jsonErrors = [];
|
|
279
263
|
schemaContent = Json.parse(content, jsonErrors);
|
|
280
|
-
|
|
264
|
+
const errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset)] : [];
|
|
281
265
|
return new UnresolvedSchema(schemaContent, errors);
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
|
|
266
|
+
}, (error) => {
|
|
267
|
+
let errorMessage = error.toString();
|
|
268
|
+
const errorSplit = error.toString().split('Error: ');
|
|
285
269
|
if (errorSplit.length > 1) {
|
|
286
270
|
// more concise error message, URL and context are attached by caller anyways
|
|
287
271
|
errorMessage = errorSplit[1];
|
|
@@ -291,52 +275,44 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
291
275
|
}
|
|
292
276
|
return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
|
|
293
277
|
});
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (schema
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
resolveErrors.push(localize('json.schema.draft201909.notsupported', "Draft 2019-09 schemas are not yet fully supported."));
|
|
306
|
-
}
|
|
307
|
-
else if (id === 'https://json-schema.org/draft/2020-12/schema') {
|
|
308
|
-
resolveErrors.push(localize('json.schema.draft202012.notsupported', "Draft 2020-12 schemas are not yet fully supported."));
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
var contextService = this.contextService;
|
|
312
|
-
var findSectionByJSONPointer = function (schema, path) {
|
|
278
|
+
}
|
|
279
|
+
resolveSchemaContent(schemaToResolve, handle) {
|
|
280
|
+
const resolveErrors = schemaToResolve.errors.slice(0);
|
|
281
|
+
const schema = schemaToResolve.schema;
|
|
282
|
+
let schemaDraft = schema.$schema ? normalizeId(schema.$schema) : undefined;
|
|
283
|
+
if (schemaDraft === 'http://json-schema.org/draft-03/schema') {
|
|
284
|
+
return this.promise.resolve(new ResolvedSchema({}, [localize('json.schema.draft03.notsupported', "Draft-03 schemas are not supported.")], [], schemaDraft));
|
|
285
|
+
}
|
|
286
|
+
let usesUnsupportedFeatures = new Set();
|
|
287
|
+
const contextService = this.contextService;
|
|
288
|
+
const findSectionByJSONPointer = (schema, path) => {
|
|
313
289
|
path = decodeURIComponent(path);
|
|
314
|
-
|
|
290
|
+
let current = schema;
|
|
315
291
|
if (path[0] === '/') {
|
|
316
292
|
path = path.substring(1);
|
|
317
293
|
}
|
|
318
|
-
path.split('/').some(
|
|
294
|
+
path.split('/').some((part) => {
|
|
319
295
|
part = part.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
320
296
|
current = current[part];
|
|
321
297
|
return !current;
|
|
322
298
|
});
|
|
323
299
|
return current;
|
|
324
300
|
};
|
|
325
|
-
|
|
301
|
+
const findSchemaById = (schema, handle, id) => {
|
|
326
302
|
if (!handle.anchors) {
|
|
327
303
|
handle.anchors = collectAnchors(schema);
|
|
328
304
|
}
|
|
329
305
|
return handle.anchors.get(id);
|
|
330
306
|
};
|
|
331
|
-
|
|
332
|
-
for (
|
|
307
|
+
const merge = (target, section) => {
|
|
308
|
+
for (const key in section) {
|
|
333
309
|
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key) && key !== 'id' && key !== '$id') {
|
|
334
310
|
target[key] = section[key];
|
|
335
311
|
}
|
|
336
312
|
}
|
|
337
313
|
};
|
|
338
|
-
|
|
339
|
-
|
|
314
|
+
const mergeRef = (target, sourceRoot, sourceHandle, refSegment) => {
|
|
315
|
+
let section;
|
|
340
316
|
if (refSegment === undefined || refSegment.length === 0) {
|
|
341
317
|
section = sourceRoot;
|
|
342
318
|
}
|
|
@@ -355,29 +331,29 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
355
331
|
resolveErrors.push(localize('json.schema.invalidid', '$ref \'{0}\' in \'{1}\' can not be resolved.', refSegment, sourceHandle.uri));
|
|
356
332
|
}
|
|
357
333
|
};
|
|
358
|
-
|
|
334
|
+
const resolveExternalLink = (node, uri, refSegment, parentHandle) => {
|
|
359
335
|
if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
|
|
360
336
|
uri = contextService.resolveRelativePath(uri, parentHandle.uri);
|
|
361
337
|
}
|
|
362
338
|
uri = normalizeId(uri);
|
|
363
|
-
|
|
364
|
-
return referencedHandle.getUnresolvedSchema().then(
|
|
339
|
+
const referencedHandle = this.getOrAddSchemaHandle(uri);
|
|
340
|
+
return referencedHandle.getUnresolvedSchema().then(unresolvedSchema => {
|
|
365
341
|
parentHandle.dependencies.add(uri);
|
|
366
342
|
if (unresolvedSchema.errors.length) {
|
|
367
|
-
|
|
343
|
+
const loc = refSegment ? uri + '#' + refSegment : uri;
|
|
368
344
|
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
|
|
369
345
|
}
|
|
370
346
|
mergeRef(node, unresolvedSchema.schema, referencedHandle, refSegment);
|
|
371
347
|
return resolveRefs(node, unresolvedSchema.schema, referencedHandle);
|
|
372
348
|
});
|
|
373
349
|
};
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
350
|
+
const resolveRefs = (node, parentSchema, parentHandle) => {
|
|
351
|
+
const openPromises = [];
|
|
352
|
+
this.traverseNodes(node, next => {
|
|
353
|
+
const seenRefs = new Set();
|
|
378
354
|
while (next.$ref) {
|
|
379
|
-
|
|
380
|
-
|
|
355
|
+
const ref = next.$ref;
|
|
356
|
+
const segments = ref.split('#', 2);
|
|
381
357
|
delete next.$ref;
|
|
382
358
|
if (segments[0].length > 0) {
|
|
383
359
|
// This is a reference to an external schema
|
|
@@ -387,110 +363,119 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
387
363
|
else {
|
|
388
364
|
// This is a reference inside the current schema
|
|
389
365
|
if (!seenRefs.has(ref)) {
|
|
390
|
-
|
|
366
|
+
const id = segments[1];
|
|
391
367
|
mergeRef(next, parentSchema, parentHandle, id);
|
|
392
368
|
seenRefs.add(ref);
|
|
393
369
|
}
|
|
394
370
|
}
|
|
395
371
|
}
|
|
372
|
+
if (next.$recursiveRef) {
|
|
373
|
+
usesUnsupportedFeatures.add('$recursiveRef');
|
|
374
|
+
}
|
|
375
|
+
if (next.$dynamicRef) {
|
|
376
|
+
usesUnsupportedFeatures.add('$dynamicRef');
|
|
377
|
+
}
|
|
396
378
|
});
|
|
397
|
-
return
|
|
379
|
+
return this.promise.all(openPromises);
|
|
398
380
|
};
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
// delete next.id;
|
|
406
|
-
var anchor = id.substring(1);
|
|
381
|
+
const collectAnchors = (root) => {
|
|
382
|
+
const result = new Map();
|
|
383
|
+
this.traverseNodes(root, next => {
|
|
384
|
+
const id = next.$id || next.id;
|
|
385
|
+
const anchor = isString(id) && id.charAt(0) === '#' ? id.substring(1) : next.$anchor;
|
|
386
|
+
if (anchor) {
|
|
407
387
|
if (result.has(anchor)) {
|
|
408
|
-
resolveErrors.push(localize('json.schema.duplicateid', 'Duplicate
|
|
388
|
+
resolveErrors.push(localize('json.schema.duplicateid', 'Duplicate anchor declaration: \'{0}\'', anchor));
|
|
409
389
|
}
|
|
410
390
|
else {
|
|
411
391
|
result.set(anchor, next);
|
|
412
392
|
}
|
|
413
393
|
}
|
|
394
|
+
if (next.$recursiveAnchor) {
|
|
395
|
+
usesUnsupportedFeatures.add('$recursiveAnchor');
|
|
396
|
+
}
|
|
397
|
+
if (next.$dynamicAnchor) {
|
|
398
|
+
usesUnsupportedFeatures.add('$dynamicAnchor');
|
|
399
|
+
}
|
|
414
400
|
});
|
|
415
401
|
return result;
|
|
416
402
|
};
|
|
417
|
-
return resolveRefs(schema, schema, handle).then(
|
|
418
|
-
|
|
403
|
+
return resolveRefs(schema, schema, handle).then(_ => {
|
|
404
|
+
let resolveWarnings = [];
|
|
405
|
+
if (usesUnsupportedFeatures.size) {
|
|
406
|
+
resolveWarnings.push(localize('json.schema.warnings', 'The schema uses meta-schema features ({0}) that are not yet supported by the validator.', Array.from(usesUnsupportedFeatures.keys()).join(', ')));
|
|
407
|
+
}
|
|
408
|
+
return new ResolvedSchema(schema, resolveErrors, resolveWarnings, schemaDraft);
|
|
419
409
|
});
|
|
420
|
-
}
|
|
421
|
-
|
|
410
|
+
}
|
|
411
|
+
traverseNodes(root, handle) {
|
|
422
412
|
if (!root || typeof root !== 'object') {
|
|
423
413
|
return Promise.resolve(null);
|
|
424
414
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
entries[_i] = arguments[_i];
|
|
430
|
-
}
|
|
431
|
-
for (var _a = 0, entries_1 = entries; _a < entries_1.length; _a++) {
|
|
432
|
-
var entry = entries_1[_a];
|
|
433
|
-
if (typeof entry === 'object') {
|
|
415
|
+
const seen = new Set();
|
|
416
|
+
const collectEntries = (...entries) => {
|
|
417
|
+
for (const entry of entries) {
|
|
418
|
+
if (isObject(entry)) {
|
|
434
419
|
toWalk.push(entry);
|
|
435
420
|
}
|
|
436
421
|
}
|
|
437
422
|
};
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (typeof map === 'object') {
|
|
446
|
-
for (var k in map) {
|
|
447
|
-
var key = k;
|
|
448
|
-
var entry = map[key];
|
|
449
|
-
if (typeof entry === 'object') {
|
|
423
|
+
const collectMapEntries = (...maps) => {
|
|
424
|
+
for (const map of maps) {
|
|
425
|
+
if (isObject(map)) {
|
|
426
|
+
for (const k in map) {
|
|
427
|
+
const key = k;
|
|
428
|
+
const entry = map[key];
|
|
429
|
+
if (isObject(entry)) {
|
|
450
430
|
toWalk.push(entry);
|
|
451
431
|
}
|
|
452
432
|
}
|
|
453
433
|
}
|
|
454
434
|
}
|
|
455
435
|
};
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
459
|
-
arrays[_i] = arguments[_i];
|
|
460
|
-
}
|
|
461
|
-
for (var _a = 0, arrays_1 = arrays; _a < arrays_1.length; _a++) {
|
|
462
|
-
var array = arrays_1[_a];
|
|
436
|
+
const collectArrayEntries = (...arrays) => {
|
|
437
|
+
for (const array of arrays) {
|
|
463
438
|
if (Array.isArray(array)) {
|
|
464
|
-
for (
|
|
465
|
-
|
|
466
|
-
if (typeof entry === 'object') {
|
|
439
|
+
for (const entry of array) {
|
|
440
|
+
if (isObject(entry)) {
|
|
467
441
|
toWalk.push(entry);
|
|
468
442
|
}
|
|
469
443
|
}
|
|
470
444
|
}
|
|
471
445
|
}
|
|
472
446
|
};
|
|
473
|
-
|
|
474
|
-
|
|
447
|
+
const collectEntryOrArrayEntries = (items) => {
|
|
448
|
+
if (Array.isArray(items)) {
|
|
449
|
+
for (const entry of items) {
|
|
450
|
+
if (isObject(entry)) {
|
|
451
|
+
toWalk.push(entry);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
else if (isObject(items)) {
|
|
456
|
+
toWalk.push(items);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const toWalk = [root];
|
|
460
|
+
let next = toWalk.pop();
|
|
475
461
|
while (next) {
|
|
476
462
|
if (!seen.has(next)) {
|
|
477
463
|
seen.add(next);
|
|
478
464
|
handle(next);
|
|
479
|
-
collectEntries(next.
|
|
480
|
-
collectMapEntries(next.definitions, next.properties, next.patternProperties, next.dependencies);
|
|
481
|
-
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.
|
|
465
|
+
collectEntries(next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else, next.unevaluatedItems, next.unevaluatedProperties);
|
|
466
|
+
collectMapEntries(next.definitions, next.$defs, next.properties, next.patternProperties, next.dependencies, next.dependentSchemas);
|
|
467
|
+
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.prefixItems);
|
|
468
|
+
collectEntryOrArrayEntries(next.items);
|
|
482
469
|
}
|
|
483
470
|
next = toWalk.pop();
|
|
484
471
|
}
|
|
485
|
-
}
|
|
472
|
+
}
|
|
486
473
|
;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
if (p.keyNode.value === '$schema' && ((_b = p.valueNode) === null || _b === void 0 ? void 0 : _b.type) === 'string') {
|
|
493
|
-
var schemaId = p.valueNode.value;
|
|
474
|
+
getSchemaFromProperty(resource, document) {
|
|
475
|
+
if (document.root?.type === 'object') {
|
|
476
|
+
for (const p of document.root.properties) {
|
|
477
|
+
if (p.keyNode.value === '$schema' && p.valueNode?.type === 'string') {
|
|
478
|
+
let schemaId = p.valueNode.value;
|
|
494
479
|
if (this.contextService && !/^\w[\w\d+.-]*:/.test(schemaId)) { // has scheme
|
|
495
480
|
schemaId = this.contextService.resolveRelativePath(schemaId, resource);
|
|
496
481
|
}
|
|
@@ -499,16 +484,14 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
499
484
|
}
|
|
500
485
|
}
|
|
501
486
|
return undefined;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
for (
|
|
508
|
-
var entry = _a[_i];
|
|
487
|
+
}
|
|
488
|
+
getAssociatedSchemas(resource) {
|
|
489
|
+
const seen = Object.create(null);
|
|
490
|
+
const schemas = [];
|
|
491
|
+
const normalizedResource = normalizeResourceForMatching(resource);
|
|
492
|
+
for (const entry of this.filePatternAssociations) {
|
|
509
493
|
if (entry.matchesPattern(normalizedResource)) {
|
|
510
|
-
for (
|
|
511
|
-
var schemaId = _c[_b];
|
|
494
|
+
for (const schemaId of entry.getURIs()) {
|
|
512
495
|
if (!seen[schemaId]) {
|
|
513
496
|
schemas.push(schemaId);
|
|
514
497
|
seen[schemaId] = true;
|
|
@@ -517,66 +500,64 @@ var JSONSchemaService = /** @class */ (function () {
|
|
|
517
500
|
}
|
|
518
501
|
}
|
|
519
502
|
return schemas;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
503
|
+
}
|
|
504
|
+
getSchemaURIsForResource(resource, document) {
|
|
505
|
+
let schemeId = document && this.getSchemaFromProperty(resource, document);
|
|
523
506
|
if (schemeId) {
|
|
524
507
|
return [schemeId];
|
|
525
508
|
}
|
|
526
509
|
return this.getAssociatedSchemas(resource);
|
|
527
|
-
}
|
|
528
|
-
|
|
510
|
+
}
|
|
511
|
+
getSchemaForResource(resource, document) {
|
|
529
512
|
if (document) {
|
|
530
513
|
// first use $schema if present
|
|
531
|
-
|
|
514
|
+
let schemeId = this.getSchemaFromProperty(resource, document);
|
|
532
515
|
if (schemeId) {
|
|
533
|
-
|
|
516
|
+
const id = normalizeId(schemeId);
|
|
534
517
|
return this.getOrAddSchemaHandle(id).getResolvedSchema();
|
|
535
518
|
}
|
|
536
519
|
}
|
|
537
520
|
if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) {
|
|
538
521
|
return this.cachedSchemaForResource.resolvedSchema;
|
|
539
522
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
this.cachedSchemaForResource = { resource
|
|
523
|
+
const schemas = this.getAssociatedSchemas(resource);
|
|
524
|
+
const resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined);
|
|
525
|
+
this.cachedSchemaForResource = { resource, resolvedSchema };
|
|
543
526
|
return resolvedSchema;
|
|
544
|
-
}
|
|
545
|
-
|
|
527
|
+
}
|
|
528
|
+
createCombinedSchema(resource, schemaIds) {
|
|
546
529
|
if (schemaIds.length === 1) {
|
|
547
530
|
return this.getOrAddSchemaHandle(schemaIds[0]);
|
|
548
531
|
}
|
|
549
532
|
else {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
allOf: schemaIds.map(
|
|
533
|
+
const combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
|
|
534
|
+
const combinedSchema = {
|
|
535
|
+
allOf: schemaIds.map(schemaId => ({ $ref: schemaId }))
|
|
553
536
|
};
|
|
554
537
|
return this.addSchemaHandle(combinedSchemaId, combinedSchema);
|
|
555
538
|
}
|
|
556
|
-
}
|
|
557
|
-
|
|
539
|
+
}
|
|
540
|
+
getMatchingSchemas(document, jsonDocument, schema) {
|
|
558
541
|
if (schema) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return handle.getResolvedSchema().then(
|
|
562
|
-
return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(
|
|
542
|
+
const id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
|
|
543
|
+
const handle = this.addSchemaHandle(id, schema);
|
|
544
|
+
return handle.getResolvedSchema().then(resolvedSchema => {
|
|
545
|
+
return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(s => !s.inverted);
|
|
563
546
|
});
|
|
564
547
|
}
|
|
565
|
-
return this.getSchemaForResource(document.uri, jsonDocument).then(
|
|
548
|
+
return this.getSchemaForResource(document.uri, jsonDocument).then(schema => {
|
|
566
549
|
if (schema) {
|
|
567
|
-
return jsonDocument.getMatchingSchemas(schema.schema).filter(
|
|
550
|
+
return jsonDocument.getMatchingSchemas(schema.schema).filter(s => !s.inverted);
|
|
568
551
|
}
|
|
569
552
|
return [];
|
|
570
553
|
});
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
export { JSONSchemaService };
|
|
575
|
-
var idCounter = 0;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
let idCounter = 0;
|
|
576
557
|
function normalizeId(id) {
|
|
577
558
|
// remove trailing '#', normalize drive capitalization
|
|
578
559
|
try {
|
|
579
|
-
return URI.parse(id).toString();
|
|
560
|
+
return URI.parse(id).toString(true);
|
|
580
561
|
}
|
|
581
562
|
catch (e) {
|
|
582
563
|
return id;
|
|
@@ -585,7 +566,7 @@ function normalizeId(id) {
|
|
|
585
566
|
function normalizeResourceForMatching(resource) {
|
|
586
567
|
// remove queries and fragments, normalize drive capitalization
|
|
587
568
|
try {
|
|
588
|
-
return URI.parse(resource).with({ fragment: null, query: null }).toString();
|
|
569
|
+
return URI.parse(resource).with({ fragment: null, query: null }).toString(true);
|
|
589
570
|
}
|
|
590
571
|
catch (e) {
|
|
591
572
|
return resource;
|
|
@@ -593,7 +574,7 @@ function normalizeResourceForMatching(resource) {
|
|
|
593
574
|
}
|
|
594
575
|
function toDisplayString(url) {
|
|
595
576
|
try {
|
|
596
|
-
|
|
577
|
+
const uri = URI.parse(url);
|
|
597
578
|
if (uri.scheme === 'file') {
|
|
598
579
|
return uri.fsPath;
|
|
599
580
|
}
|