1
1
import * as JsonPointer from "@hyperjump/json-pointer" ;
2
2
import { JsonLexer } from "./json-lexer.js" ;
3
+ import { parse as parseYaml } from "yaml-unist-parser" ;
3
4
4
5
/**
5
6
* @import { Node, Position } from "unist"
@@ -9,52 +10,59 @@ import { JsonLexer } from "./json-lexer.js";
9
10
* JsonNode,
10
11
* JsonObjectNode,
11
12
* JsonPropertyNameNode,
12
- * JsonPropertyNode
13
+ * JsonPropertyNode,
14
+ * JsonStringNode
13
15
* } from "./jsonast.d.ts"
16
+ * @import {
17
+ * YamlUnistNode,
18
+ * MappingKey,
19
+ * MappingItem,
20
+ * FlowMappingItem,
21
+ * ContentNode
22
+ * } from "yaml-unist-parser"
14
23
*/
15
24
16
- /** @type (json: string, uri?: string ) => JsonNode */
17
- export const fromJson = ( json , uri = "" ) => {
25
+ /** @type (json: string) => JsonNode */
26
+ export const fromJson = ( json ) => {
18
27
const lexer = new JsonLexer ( json ) ;
19
28
20
29
const token = lexer . nextToken ( ) ;
21
- const jsonValue = parseValue ( token , lexer , undefined , uri , "" ) ;
30
+ const jsonValue = parseValue ( token , lexer , undefined ) ;
22
31
23
32
lexer . done ( ) ;
24
33
25
34
return jsonValue ;
26
35
} ;
27
36
28
- /** @type (token: JsonToken, lexer: JsonLexer, key: string | undefined, uri: string, pointer: string ) => JsonNode */
29
- const parseValue = ( token , lexer , _key , uri , pointer ) => {
37
+ /** @type (token: JsonToken, lexer: JsonLexer, key: string | undefined) => JsonNode */
38
+ const parseValue = ( token , lexer , _key ) => {
30
39
switch ( token . type ) {
31
40
case "null" :
32
41
case "boolean" :
33
42
case "number" :
34
43
case "string" :
35
- return parseScalar ( token , uri , pointer ) ;
44
+ return parseScalar ( token ) ;
36
45
case "[" :
37
- return parseArray ( token , lexer , uri , pointer ) ;
46
+ return parseArray ( token , lexer ) ;
38
47
case "{" :
39
- return parseObject ( token , lexer , uri , pointer ) ;
48
+ return parseObject ( token , lexer ) ;
40
49
default :
41
50
throw lexer . syntaxError ( "Expected a JSON value" , token ) ;
42
51
}
43
52
} ;
44
53
45
- /** @type (token: JsonToken<"null" | "boolean" | "number" | "string">, uri: string, pointer: string ) => JsonNode */
46
- const parseScalar = ( token , uri , pointer ) => {
54
+ /** @type (token: JsonToken<"null" | "boolean" | "number" | "string">) => JsonNode */
55
+ const parseScalar = ( token ) => {
47
56
return {
48
57
type : "json" ,
49
58
jsonType : token . type ,
50
59
value : JSON . parse ( token . value ) , // eslint-disable-line @typescript-eslint/no-unsafe-assignment
51
- location : `${ uri } #${ encodeURI ( pointer ) } ` ,
52
60
position : tokenPosition ( token )
53
61
} ;
54
62
} ;
55
63
56
- /** @type (token: JsonToken, lexer: JsonLexer, key: string, uri: string, pointer: string ) => JsonPropertyNode */
57
- const parseProperty = ( token , lexer , _key , uri , pointer ) => {
64
+ /** @type (token: JsonToken, lexer: JsonLexer, key: string) => JsonPropertyNode */
65
+ const parseProperty = ( token , lexer , _key ) => {
58
66
if ( token . type !== "string" ) {
59
67
throw lexer . syntaxError ( "Expected a propertry" , token ) ;
60
68
}
@@ -71,7 +79,7 @@ const parseProperty = (token, lexer, _key, uri, pointer) => {
71
79
throw lexer . syntaxError ( "Expected :" , token ) ;
72
80
}
73
81
74
- const valueNode = parseValue ( lexer . nextToken ( ) , lexer , keyNode . value , uri , JsonPointer . append ( keyNode . value , pointer ) ) ;
82
+ const valueNode = parseValue ( lexer . nextToken ( ) , lexer , keyNode . value ) ;
75
83
76
84
return {
77
85
type : "json-property" ,
@@ -90,11 +98,11 @@ const parseProperty = (token, lexer, _key, uri, pointer) => {
90
98
91
99
/**
92
100
* @type <P extends ParentNode<C>, C extends JsonNode | JsonPropertyNode>(
93
- * parseChild: (token: JsonToken, lexer: JsonLexer, key: string, uri: string, pointer: string ) => C,
101
+ * parseChild: (token: JsonToken, lexer: JsonLexer, key: string) => C,
94
102
* endToken: string
95
- * ) => (lexer: JsonLexer, node: P, uri: string, pointer: string ) => P
103
+ * ) => (lexer: JsonLexer, node: P) => P
96
104
*/
97
- const parseCommaSeparated = ( parseChild , endToken ) => ( lexer , node , uri , pointer ) => {
105
+ const parseCommaSeparated = ( parseChild , endToken ) => ( lexer , node ) => {
98
106
for ( let index = 0 ; true ; index ++ ) {
99
107
let token = lexer . nextToken ( ) ;
100
108
@@ -111,44 +119,42 @@ const parseCommaSeparated = (parseChild, endToken) => (lexer, node, uri, pointer
111
119
}
112
120
}
113
121
114
- const childNode = parseChild ( token , lexer , `${ index } ` , uri , pointer ) ;
122
+ const childNode = parseChild ( token , lexer , `${ index } ` ) ;
115
123
if ( childNode ) {
116
124
node . children . push ( childNode ) ;
117
125
}
118
126
}
119
127
} ;
120
128
121
- /** @type (openToken: JsonToken, lexer: JsonLexer, uri: string, pointer: string ) => JsonArrayNode */
122
- const parseArray = ( openToken , lexer , uri , pointer ) => {
129
+ /** @type (openToken: JsonToken, lexer: JsonLexer) => JsonArrayNode */
130
+ const parseArray = ( openToken , lexer ) => {
123
131
return parseItems ( lexer , {
124
132
type : "json" ,
125
133
jsonType : "array" ,
126
134
children : [ ] ,
127
- location : `${ uri } #${ encodeURI ( pointer ) } ` ,
128
135
position : tokenPosition ( openToken )
129
- } , uri , pointer ) ;
136
+ } ) ;
130
137
} ;
131
138
132
- /** @type (token: JsonToken, lexer: JsonLexer, key: string, uri: string, pointer: string ) => JsonNode */
133
- const parseItem = ( token , lexer , key , uri , pointer ) => {
134
- return parseValue ( token , lexer , key , uri , JsonPointer . append ( key , pointer ) ) ;
139
+ /** @type (token: JsonToken, lexer: JsonLexer, key: string) => JsonNode */
140
+ const parseItem = ( token , lexer , key ) => {
141
+ return parseValue ( token , lexer , key ) ;
135
142
} ;
136
143
137
- /** @type (lexer: JsonLexer, node: { type: "json" } & JsonArrayNode, uri: string, pointer: string ) => JsonArrayNode */
144
+ /** @type (lexer: JsonLexer, node: { type: "json" } & JsonArrayNode) => JsonArrayNode */
138
145
const parseItems = parseCommaSeparated ( parseItem , "]" ) ;
139
146
140
- /** @type (openToken: JsonToken, lexer: JsonLexer, uri: string, pointer: string ) => JsonObjectNode */
141
- const parseObject = ( openToken , lexer , uri , pointer ) => {
147
+ /** @type (openToken: JsonToken, lexer: JsonLexer) => JsonObjectNode */
148
+ const parseObject = ( openToken , lexer ) => {
142
149
return parseProperties ( lexer , {
143
150
type : "json" ,
144
151
jsonType : "object" ,
145
152
children : [ ] ,
146
- location : `${ uri } #${ encodeURI ( pointer ) } ` ,
147
153
position : tokenPosition ( openToken )
148
- } , uri , pointer ) ;
154
+ } ) ;
149
155
} ;
150
156
151
- /** @type (lexer: JsonLexer, node: { type: "json" } & JsonObjectNode, uri: string, pointer: string ) => JsonObjectNode */
157
+ /** @type (lexer: JsonLexer, node: { type: "json" } & JsonObjectNode) => JsonObjectNode */
152
158
const parseProperties = parseCommaSeparated ( parseProperty , "}" ) ;
153
159
154
160
/** @type (startToken: JsonToken, endToken?: JsonToken) => Position */
@@ -211,3 +217,121 @@ export const getNodeFromPointer = (tree, pointer, returnProperty) => {
211
217
212
218
return node . type === "json-property" && ! returnProperty ? node . children [ 1 ] : node ;
213
219
} ;
220
+
221
+ /** @type (yaml: string) => JsonNode */
222
+ export const fromYaml = ( yaml ) => {
223
+ const root = parseYaml ( yaml ) ;
224
+ return yamlToJson ( root ) ;
225
+ } ;
226
+
227
+ /**
228
+ * @overload
229
+ * @param {MappingItem | FlowMappingItem } yamlNode
230
+ * @returns {JsonPropertyNode }
231
+ *
232
+ * @overload
233
+ * @param {MappingKey } yamlNode
234
+ * @returns {JsonPropertyNameNode }
235
+ *
236
+ * @overload
237
+ * @param {ContentNode } yamlNode
238
+ * @returns {JsonStringNode }
239
+ *
240
+ * @overload
241
+ * @param {YamlUnistNode } yamlNode
242
+ * @returns {JsonNode }
243
+ *
244
+ * @param {YamlUnistNode } yamlNode
245
+ * @returns {JsonNode | JsonPropertyNode | JsonPropertyNameNode }
246
+ */
247
+ const yamlToJson = ( yamlNode ) => {
248
+ switch ( yamlNode . type ) {
249
+ case "root" :
250
+ return yamlToJson ( yamlNode . children [ 0 ] ) ;
251
+
252
+ case "document" :
253
+ return yamlToJson ( yamlNode . children [ 1 ] ) ;
254
+
255
+ case "documentHead" :
256
+ throw Error ( `Not Implemented - ${ yamlNode . type } ` ) ;
257
+
258
+ case "documentBody" :
259
+ if ( yamlNode . children . length === 0 ) {
260
+ throw Error ( "YAML documents must contain a value" ) ;
261
+ }
262
+ return yamlToJson ( yamlNode . children [ 0 ] ) ;
263
+
264
+ case "plain" :
265
+ case "quoteDouble" :
266
+ case "quoteSingle" :
267
+ case "blockLiteral" :
268
+ case "blockFolded" :
269
+ /** @type JsonStringNode */
270
+ const stringNode = {
271
+ type : "json" ,
272
+ jsonType : "string" ,
273
+ value : yamlNode . value ,
274
+ position : yamlNode . position
275
+ } ;
276
+ return stringNode ;
277
+
278
+ case "mapping" :
279
+ case "flowMapping" :
280
+ /** @type JsonObjectNode */
281
+ const objectNode = {
282
+ type : "json" ,
283
+ jsonType : "object" ,
284
+ children : yamlNode . children . map ( ( mappingItemNode ) => yamlToJson ( mappingItemNode ) ) ,
285
+ position : yamlNode . position
286
+ } ;
287
+ return objectNode ;
288
+
289
+ case "mappingItem" :
290
+ case "flowMappingItem" :
291
+ const [ mappingKeyNode , mappingValueNode ] = yamlNode . children ;
292
+
293
+ /** @type JsonPropertyNode */
294
+ const propertyNode = {
295
+ type : "json-property" ,
296
+ children : [
297
+ yamlToJson ( mappingKeyNode ) ,
298
+ yamlToJson ( mappingValueNode )
299
+ ] ,
300
+ position : yamlNode . position
301
+ } ;
302
+
303
+ return propertyNode ;
304
+
305
+ case "mappingKey" :
306
+ const contentNode = yamlToJson ( /** @type ContentNode */ ( yamlNode . children [ 0 ] ) ) ;
307
+ /** @type JsonPropertyNameNode */
308
+ const propertyNameNode = {
309
+ type : "json-property-name" ,
310
+ jsonType : "string" ,
311
+ value : contentNode . value ,
312
+ position : yamlNode . position
313
+ } ;
314
+ return propertyNameNode ;
315
+
316
+ case "mappingValue" :
317
+ return yamlToJson ( /** @type ContentNode */ ( yamlNode . children [ 0 ] ) ) ;
318
+
319
+ case "sequence" :
320
+ case "flowSequence" :
321
+ /** @type JsonArrayNode */
322
+ const arrayNode = {
323
+ type : "json" ,
324
+ jsonType : "array" ,
325
+ children : yamlNode . children . map ( ( sequenceItemNode ) => yamlToJson ( sequenceItemNode ) ) ,
326
+ position : yamlNode . position
327
+ } ;
328
+ return arrayNode ;
329
+
330
+ case "sequenceItem" :
331
+ case "flowSequenceItem" :
332
+ return yamlToJson ( /** @type ContentNode */ ( yamlNode . children [ 0 ] ) ) ;
333
+
334
+ default :
335
+ throw Error ( `YAML error. ${ yamlNode . type } ` ) ;
336
+ }
337
+ } ;
0 commit comments