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