@@ -6,177 +6,168 @@ import * as JsonNode from "./json-node.js";
6
6
import { uriFragment } from "./util.js" ;
7
7
8
8
9
- export class JsonSchemaDocument {
10
- constructor ( textDocument ) {
11
- this . textDocument = textDocument ;
12
- this . schemaResources = [ ] ;
13
- this . errors = [ ] ;
14
- }
15
-
16
- static async fromTextDocument ( textDocument , contextDialectUri ) {
17
- const document = new JsonSchemaDocument ( textDocument ) ;
18
-
19
- const json = textDocument . getText ( ) ;
20
- if ( json ) {
21
- const root = parseTree ( json , [ ] , {
22
- disallowComments : false ,
23
- allowTrailingComma : true ,
24
- allowEmptyContent : true
25
- } ) ;
26
-
27
- document . #buildSchemaResources( root , textDocument . uri , contextDialectUri ) ;
28
-
29
- for ( const { dialectUri, schemaResource } of document . schemaResources ) {
30
- if ( ! hasDialect ( dialectUri ) ) {
31
- const $schema = JsonNode . get ( "#/$schema" , schemaResource ) ;
32
- if ( $schema && JsonNode . typeOf ( $schema ) === "string" ) {
33
- document . errors . push ( {
34
- keyword : "https://json-schema.org/keyword/schema" ,
35
- instanceNode : $schema ,
36
- message : "Unknown dialect"
37
- } ) ;
38
- } else if ( dialectUri ) {
39
- document . errors . push ( {
40
- keyword : "https://json-schema.org/keyword/schema" ,
41
- instanceNode : schemaResource ,
42
- message : "Unknown dialect"
43
- } ) ;
44
- } else {
45
- document . errors . push ( {
46
- keyword : "https://json-schema.org/keyword/schema" ,
47
- instanceNode : schemaResource ,
48
- message : "No dialect"
49
- } ) ;
50
- }
9
+ const cons = ( textDocument ) => {
10
+ return {
11
+ textDocument : textDocument ,
12
+ schemaResources : [ ] ,
13
+ errors : [ ]
14
+ } ;
15
+ } ;
51
16
52
- continue ;
17
+ export const fromTextDocument = async ( textDocument , contextDialectUri ) => {
18
+ const document = cons ( textDocument ) ;
19
+
20
+ const json = textDocument . getText ( ) ;
21
+ if ( json ) {
22
+ const root = parseTree ( json , [ ] , {
23
+ disallowComments : false ,
24
+ allowTrailingComma : true ,
25
+ allowEmptyContent : true
26
+ } ) ;
27
+
28
+ buildSchemaResources ( document , root , textDocument . uri , contextDialectUri ) ;
29
+
30
+ for ( const { dialectUri, schemaResource } of document . schemaResources ) {
31
+ if ( ! hasDialect ( dialectUri ) ) {
32
+ const $schema = JsonNode . get ( "#/$schema" , schemaResource ) ;
33
+ if ( $schema && JsonNode . typeOf ( $schema ) === "string" ) {
34
+ document . errors . push ( {
35
+ keyword : "https://json-schema.org/keyword/schema" ,
36
+ instanceNode : $schema ,
37
+ message : "Unknown dialect"
38
+ } ) ;
39
+ } else if ( dialectUri ) {
40
+ document . errors . push ( {
41
+ keyword : "https://json-schema.org/keyword/schema" ,
42
+ instanceNode : schemaResource ,
43
+ message : "Unknown dialect"
44
+ } ) ;
45
+ } else {
46
+ document . errors . push ( {
47
+ keyword : "https://json-schema.org/keyword/schema" ,
48
+ instanceNode : schemaResource ,
49
+ message : "No dialect"
50
+ } ) ;
53
51
}
54
52
55
- const schema = await getSchema ( dialectUri ) ;
56
- const compiled = await compile ( schema ) ;
57
- const output = interpret ( compiled , schemaResource , BASIC ) ;
58
- if ( ! output . valid ) {
59
- for ( const error of output . errors ) {
60
- document . errors . push ( {
61
- keyword : error . keyword ,
62
- keywordNode : await getSchema ( error . absoluteKeywordLocation ) ,
63
- instanceNode : JsonNode . get ( error . instanceLocation , schemaResource )
64
- } ) ;
65
- }
53
+ continue ;
54
+ }
55
+
56
+ const schema = await getSchema ( dialectUri ) ;
57
+ const compiled = await compile ( schema ) ;
58
+ const output = interpret ( compiled , schemaResource , BASIC ) ;
59
+ if ( ! output . valid ) {
60
+ for ( const error of output . errors ) {
61
+ document . errors . push ( {
62
+ keyword : error . keyword ,
63
+ keywordNode : await getSchema ( error . absoluteKeywordLocation ) ,
64
+ instanceNode : JsonNode . get ( error . instanceLocation , schemaResource )
65
+ } ) ;
66
66
}
67
67
}
68
68
}
69
-
70
- return document ;
71
69
}
72
70
73
- #buildSchemaResources( node , uri = "" , dialectUri = "" , pointer = "" , parent = undefined , anchors = { } ) {
74
- const jsonNode = JsonNode . cons ( uri , pointer , getNodeValue ( node ) , node . type , [ ] , parent , node . offset , node . length ) ;
75
-
76
- switch ( node . type ) {
77
- case "array" :
78
- jsonNode . children = node . children . map ( ( child , index ) => {
79
- const itemPointer = JsonPointer . append ( index , pointer ) ;
80
- return this . #buildSchemaResources( child , uri , dialectUri , itemPointer , jsonNode , anchors ) ;
81
- } ) ;
82
- break ;
83
-
84
- case "object" :
85
- if ( pointer === "" ) {
86
- // Resource root
87
- const $schema = nodeStep ( node , "$schema" ) ;
88
- if ( $schema ?. type === "string" ) {
89
- try {
90
- dialectUri = toAbsoluteIri ( getNodeValue ( $schema ) ) ;
91
- } catch ( error ) {
92
- // Ignore
93
- }
94
- }
95
-
96
- const idToken = keywordNameFor ( "https://json-schema.org/keyword/id" , dialectUri ) ;
97
- const $idNode = idToken && nodeStep ( node , idToken ) ;
98
- if ( $idNode ) {
99
- uri = toAbsoluteIri ( resolveIri ( getNodeValue ( $idNode ) , uri ) ) ;
100
- jsonNode . baseUri = uri ;
101
- }
71
+ return document ;
72
+ } ;
102
73
103
- const legacyIdToken = keywordNameFor ( "https://json-schema.org/keyword/draft-04/id" , dialectUri ) ;
104
- const legacy$idNode = legacyIdToken && nodeStep ( node , legacyIdToken ) ;
105
- if ( legacy$idNode ?. type === "string" ) {
106
- const legacy$id = getNodeValue ( legacy$idNode ) ;
107
- if ( legacy$id [ 0 ] !== "#" ) {
108
- uri = toAbsoluteIri ( resolveIri ( legacy$id , uri ) ) ;
109
- jsonNode . baseUri = uri ;
110
- }
111
- }
112
- } else {
113
- // Check for embedded schema
114
- const embeddedDialectUri = getEmbeddedDialectUri ( node , dialectUri ) ;
115
- if ( embeddedDialectUri ) {
116
- this . #buildSchemaResources( node , uri , embeddedDialectUri ) ;
74
+ const buildSchemaResources = ( document , node , uri = "" , dialectUri = "" , pointer = "" , parent = undefined , anchors = { } ) => {
75
+ const jsonNode = JsonNode . cons ( uri , pointer , getNodeValue ( node ) , node . type , [ ] , parent , node . offset , node . length ) ;
117
76
118
- return JsonNode . cons ( uri , pointer , true , "boolean" , [ ] , parent , node . offset , node . length ) ;
77
+ switch ( node . type ) {
78
+ case "array" :
79
+ jsonNode . children = node . children . map ( ( child , index ) => {
80
+ const itemPointer = JsonPointer . append ( index , pointer ) ;
81
+ return buildSchemaResources ( document , child , uri , dialectUri , itemPointer , jsonNode , anchors ) ;
82
+ } ) ;
83
+ break ;
84
+
85
+ case "object" :
86
+ if ( pointer === "" ) {
87
+ // Resource root
88
+ const $schema = nodeStep ( node , "$schema" ) ;
89
+ if ( $schema ?. type === "string" ) {
90
+ try {
91
+ dialectUri = toAbsoluteIri ( getNodeValue ( $schema ) ) ;
92
+ } catch ( error ) {
93
+ // Ignore
119
94
}
120
95
}
121
96
122
- const anchorToken = keywordNameFor ( "https://json-schema.org/keyword/anchor " , dialectUri ) ;
123
- const $anchorNode = anchorToken && nodeStep ( node , anchorToken ) ;
124
- if ( $anchorNode ) {
125
- const anchor = getNodeValue ( $anchorNode ) ;
126
- anchors [ anchor ] = pointer ;
97
+ const idToken = keywordNameFor ( "https://json-schema.org/keyword/id " , dialectUri ) ;
98
+ const $idNode = idToken && nodeStep ( node , idToken ) ;
99
+ if ( $idNode ) {
100
+ uri = toAbsoluteIri ( resolveIri ( getNodeValue ( $idNode ) , uri ) ) ;
101
+ jsonNode . baseUri = uri ;
127
102
}
128
103
129
- const legacyAnchorToken = keywordNameFor ( "https://json-schema.org/keyword/draft-04/id" , dialectUri ) ;
130
- const legacyAnchorNode = legacyAnchorToken && nodeStep ( node , legacyAnchorToken ) ;
131
- if ( legacyAnchorNode ) {
132
- const anchor = getNodeValue ( legacyAnchorNode ) ;
133
- if ( anchor [ 0 ] === "#" ) {
134
- anchors [ uriFragment ( anchor ) ] = pointer ;
104
+ const legacyIdToken = keywordNameFor ( "https://json-schema.org/keyword/draft-04/id" , dialectUri ) ;
105
+ const legacy$idNode = legacyIdToken && nodeStep ( node , legacyIdToken ) ;
106
+ if ( legacy$idNode ?. type === "string" ) {
107
+ const legacy$id = getNodeValue ( legacy$idNode ) ;
108
+ if ( legacy$id [ 0 ] !== "#" ) {
109
+ uri = toAbsoluteIri ( resolveIri ( legacy$id , uri ) ) ;
110
+ jsonNode . baseUri = uri ;
135
111
}
136
112
}
113
+ } else {
114
+ // Check for embedded schema
115
+ const embeddedDialectUri = getEmbeddedDialectUri ( node , dialectUri ) ;
116
+ if ( embeddedDialectUri ) {
117
+ buildSchemaResources ( document , node , uri , embeddedDialectUri ) ;
137
118
138
- for ( const child of node . children ) {
139
- const propertyPointer = JsonPointer . append ( getNodeValue ( child . children [ 0 ] ) , pointer ) ;
140
- const propertyNode = this . #buildSchemaResources ( child , uri , dialectUri , propertyPointer , jsonNode , anchors ) ;
119
+ return JsonNode . cons ( uri , pointer , true , "boolean" , [ ] , parent , node . offset , node . length ) ;
120
+ }
121
+ }
141
122
142
- if ( propertyNode ) {
143
- jsonNode . children . push ( propertyNode ) ;
144
- }
123
+ const anchorToken = keywordNameFor ( "https://json-schema.org/keyword/anchor" , dialectUri ) ;
124
+ const $anchorNode = anchorToken && nodeStep ( node , anchorToken ) ;
125
+ if ( $anchorNode ) {
126
+ const anchor = getNodeValue ( $anchorNode ) ;
127
+ anchors [ anchor ] = pointer ;
128
+ }
129
+
130
+ const legacyAnchorToken = keywordNameFor ( "https://json-schema.org/keyword/draft-04/id" , dialectUri ) ;
131
+ const legacyAnchorNode = legacyAnchorToken && nodeStep ( node , legacyAnchorToken ) ;
132
+ if ( legacyAnchorNode ) {
133
+ const anchor = getNodeValue ( legacyAnchorNode ) ;
134
+ if ( anchor [ 0 ] === "#" ) {
135
+ anchors [ uriFragment ( anchor ) ] = pointer ;
145
136
}
146
- break ;
137
+ }
138
+
139
+ for ( const child of node . children ) {
140
+ const propertyPointer = JsonPointer . append ( getNodeValue ( child . children [ 0 ] ) , pointer ) ;
141
+ const propertyNode = buildSchemaResources ( document , child , uri , dialectUri , propertyPointer , jsonNode , anchors ) ;
147
142
148
- case "property" :
149
- if ( node . children . length !== 2 ) {
150
- return ;
143
+ if ( propertyNode ) {
144
+ jsonNode . children . push ( propertyNode ) ;
151
145
}
146
+ }
147
+ break ;
152
148
153
- jsonNode . children = node . children . map ( ( child ) => {
154
- return this . #buildSchemaResources( child , uri , dialectUri , pointer , jsonNode , anchors ) ;
155
- } ) ;
156
- break ;
157
- }
149
+ case "property" :
150
+ if ( node . children . length !== 2 ) {
151
+ return ;
152
+ }
158
153
159
- if ( jsonNode . pointer === "" ) {
160
- this . schemaResources . push ( {
161
- schemaResource : jsonNode ,
162
- dialectUri : dialectUri ,
163
- baseUri : uri ,
164
- anchors : anchors
154
+ jsonNode . children = node . children . map ( ( child ) => {
155
+ return buildSchemaResources ( document , child , uri , dialectUri , pointer , jsonNode , anchors ) ;
165
156
} ) ;
166
- }
167
-
168
- return jsonNode ;
157
+ break ;
169
158
}
170
159
171
- findNodeAtOffset ( offset ) {
172
- for ( const { schemaResource } of this . schemaResources ) {
173
- const node = _findNodeAtOffset ( schemaResource , offset ) ;
174
- if ( node ) {
175
- return node ;
176
- }
177
- }
160
+ if ( jsonNode . pointer === "" ) {
161
+ document . schemaResources . push ( {
162
+ schemaResource : jsonNode ,
163
+ dialectUri : dialectUri ,
164
+ baseUri : uri ,
165
+ anchors : anchors
166
+ } ) ;
178
167
}
179
- }
168
+
169
+ return jsonNode ;
170
+ } ;
180
171
181
172
const getEmbeddedDialectUri = ( node , dialectUri ) => {
182
173
const $schema = nodeStep ( node , "$schema" ) ;
@@ -202,6 +193,15 @@ const getEmbeddedDialectUri = (node, dialectUri) => {
202
193
}
203
194
} ;
204
195
196
+ export const findNodeAtOffset = ( document , offset ) => {
197
+ for ( const { schemaResource } of document . schemaResources ) {
198
+ const node = _findNodeAtOffset ( schemaResource , offset ) ;
199
+ if ( node ) {
200
+ return node ;
201
+ }
202
+ }
203
+ } ;
204
+
205
205
const _findNodeAtOffset = ( node , offset , includeRightBound = false ) => {
206
206
if ( contains ( node , offset , includeRightBound ) ) {
207
207
for ( let i = 0 ; i < node . children . length && node . children [ i ] . offset <= offset ; i ++ ) {
0 commit comments