jsonc-effect 0.2.0 → 0.3.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/index.js CHANGED
@@ -1,1454 +1,11 @@
1
- import { Chunk, Data, Effect, Function, Option, ParseResult, Schema, Stream } from "effect";
2
- const findNode = Function.dual(2, (root, path)=>Effect.sync(()=>findNodeImpl(root, path)));
3
- const findNodeAtOffset = Function.dual(2, (root, offset)=>Effect.sync(()=>findNodeAtOffsetImpl(root, offset)));
4
- const getNodePath = Function.dual(2, (root, targetOffset)=>Effect.sync(()=>buildPath(root, targetOffset, [])));
5
- const getNodeValue = (node)=>Effect.sync(()=>evaluateNode(node));
6
- function findNodeImpl(root, path) {
7
- let current = root;
8
- for (const segment of path){
9
- if (!current?.children) return Option.none();
10
- if ("string" == typeof segment) {
11
- if ("object" !== current.type) return Option.none();
12
- const property = current.children.find((child)=>"property" === child.type && void 0 !== child.children && child.children[0]?.value === segment);
13
- current = property?.children?.[1];
14
- } else {
15
- if ("array" !== current.type) return Option.none();
16
- current = current.children[segment];
17
- }
18
- }
19
- return current ? Option.some(current) : Option.none();
20
- }
21
- function findNodeAtOffsetImpl(root, offset) {
22
- if (offset < root.offset || offset >= root.offset + root.length) return Option.none();
23
- if (!root.children) return Option.some(root);
24
- for (const child of root.children)if (offset >= child.offset && offset < child.offset + child.length) return findNodeAtOffsetImpl(child, offset);
25
- return Option.some(root);
26
- }
27
- function buildPath(node, targetOffset, currentPath) {
28
- if (targetOffset < node.offset || targetOffset >= node.offset + node.length) return Option.none();
29
- if (!node.children) return Option.some(currentPath);
30
- if ("object" === node.type) {
31
- for (const prop of node.children)if ("property" === prop.type && void 0 !== prop.children && targetOffset >= prop.offset && targetOffset < prop.offset + prop.length) {
32
- const key = prop.children[0]?.value;
33
- const valuePath = [
34
- ...currentPath,
35
- key
36
- ];
37
- const valueChild = prop.children[1];
38
- if (valueChild && targetOffset >= valueChild.offset && targetOffset < valueChild.offset + valueChild.length) return buildPath(valueChild, targetOffset, valuePath);
39
- return Option.some(valuePath);
40
- }
41
- } else if ("array" === node.type) for(let i = 0; i < node.children.length; i++){
42
- const child = node.children[i];
43
- if (targetOffset >= child.offset && targetOffset < child.offset + child.length) return buildPath(child, targetOffset, [
44
- ...currentPath,
45
- i
46
- ]);
47
- }
48
- return Option.some(currentPath);
49
- }
50
- function evaluateNode(node) {
51
- switch(node.type){
52
- case "object":
53
- {
54
- const obj = {};
55
- if (node.children) {
56
- for (const prop of node.children)if ("property" === prop.type && void 0 !== prop.children && 2 === prop.children.length) {
57
- const key = prop.children[0].value;
58
- obj[key] = evaluateNode(prop.children[1]);
59
- }
60
- }
61
- return obj;
62
- }
63
- case "array":
64
- return (node.children ?? []).map(evaluateNode);
65
- case "property":
66
- return node.children?.[1] ? evaluateNode(node.children[1]) : void 0;
67
- case "string":
68
- case "number":
69
- case "boolean":
70
- case "null":
71
- return node.value;
72
- }
73
- }
74
- const JsoncParseErrorCode = Schema.Literal("InvalidSymbol", "InvalidNumberFormat", "PropertyNameExpected", "ValueExpected", "ColonExpected", "CommaExpected", "CloseBraceExpected", "CloseBracketExpected", "EndOfFileExpected", "InvalidCommentToken", "UnexpectedEndOfComment", "UnexpectedEndOfString", "UnexpectedEndOfNumber", "InvalidUnicode", "InvalidEscapeCharacter", "InvalidCharacter");
75
- class JsoncParseErrorDetail extends Schema.Class("JsoncParseErrorDetail")({
76
- code: JsoncParseErrorCode,
77
- message: Schema.String,
78
- offset: Schema.Number,
79
- length: Schema.Number,
80
- startLine: Schema.Number,
81
- startCharacter: Schema.Number
82
- }) {
83
- }
84
- const JsoncParseErrorBase = Data.TaggedError("JsoncParseError");
85
- class JsoncParseError extends JsoncParseErrorBase {
86
- get message() {
87
- const count = this.errors.length;
88
- return `JSONC parse failed with ${count} error${1 !== count ? "s" : ""}: ${this.errors.map((e)=>e.message).join("; ")}`;
89
- }
90
- }
91
- const JsoncNodeNotFoundErrorBase = Data.TaggedError("JsoncNodeNotFoundError");
92
- class JsoncNodeNotFoundError extends JsoncNodeNotFoundErrorBase {
93
- get message() {
94
- return `Node not found at path [${this.path.join(", ")}] in ${this.rootNodeType} node`;
95
- }
96
- }
97
- const JsoncModificationErrorBase = Data.TaggedError("JsoncModificationError");
98
- class JsoncModificationError extends JsoncModificationErrorBase {
99
- get message() {
100
- return `Modification failed at path [${this.path.join(", ")}]: ${this.reason}`;
101
- }
102
- }
103
- const isWhitespace = (ch)=>0x20 === ch || 0x09 === ch || 0x0b === ch || 0x0c === ch || 0xa0 === ch || 0xfeff === ch;
104
- const isLineBreak = (ch)=>0x0a === ch || 0x0d === ch || 0x2028 === ch || 0x2029 === ch;
105
- const isDigit = (ch)=>ch >= 0x30 && ch <= 0x39;
106
- const createScanner = (text, ignoreTrivia = false)=>{
107
- const len = text.length;
108
- let pos = 0;
109
- let tokenOffset = 0;
110
- let token = "Unknown";
111
- let tokenValue = "";
112
- let tokenError = "None";
113
- let lineNumber = 0;
114
- let lineStartOffset = 0;
115
- let tokenStartLine = 0;
116
- let tokenStartCharacter = 0;
117
- const scanHexDigits = (count)=>{
118
- let value = 0;
119
- for(let i = 0; i < count; i++){
120
- if (pos >= len) return -1;
121
- const ch = text.charCodeAt(pos);
122
- if (ch >= 0x30 && ch <= 0x39) value = 16 * value + (ch - 0x30);
123
- else if (ch >= 0x41 && ch <= 0x46) value = 16 * value + (ch - 0x41 + 10);
124
- else {
125
- if (!(ch >= 0x61) || !(ch <= 0x66)) return -1;
126
- value = 16 * value + (ch - 0x61 + 10);
127
- }
128
- pos++;
129
- }
130
- return value;
131
- };
132
- const scanString = ()=>{
133
- let result = "";
134
- pos++;
135
- let start = pos;
136
- while(pos < len){
137
- const ch = text.charCodeAt(pos);
138
- if (0x22 === ch) {
139
- result += text.substring(start, pos);
140
- pos++;
141
- return result;
142
- }
143
- if (0x5c === ch) {
144
- result += text.substring(start, pos);
145
- pos++;
146
- if (pos >= len) {
147
- tokenError = "UnexpectedEndOfString";
148
- return result;
149
- }
150
- const escaped = text.charCodeAt(pos);
151
- pos++;
152
- switch(escaped){
153
- case 0x22:
154
- result += '"';
155
- break;
156
- case 0x5c:
157
- result += "\\";
158
- break;
159
- case 0x2f:
160
- result += "/";
161
- break;
162
- case 0x62:
163
- result += "\b";
164
- break;
165
- case 0x66:
166
- result += "\f";
167
- break;
168
- case 0x6e:
169
- result += "\n";
170
- break;
171
- case 0x72:
172
- result += "\r";
173
- break;
174
- case 0x74:
175
- result += "\t";
176
- break;
177
- case 0x75:
178
- {
179
- const value = scanHexDigits(4);
180
- if (value >= 0) result += String.fromCharCode(value);
181
- else tokenError = "InvalidUnicode";
182
- break;
183
- }
184
- default:
185
- tokenError = "InvalidEscapeCharacter";
186
- break;
187
- }
188
- start = pos;
189
- } else if (isLineBreak(ch)) {
190
- tokenError = "UnexpectedEndOfString";
191
- return result + text.substring(start, pos);
192
- } else pos++;
193
- }
194
- tokenError = "UnexpectedEndOfString";
195
- return result + text.substring(start, pos);
196
- };
197
- const scanNumber = ()=>{
198
- const start = pos;
199
- if (0x2d === text.charCodeAt(pos)) pos++;
200
- if (0x30 === text.charCodeAt(pos)) pos++;
201
- else {
202
- if (!isDigit(text.charCodeAt(pos))) {
203
- tokenError = "UnexpectedEndOfNumber";
204
- return text.substring(start, pos);
205
- }
206
- pos++;
207
- while(pos < len && isDigit(text.charCodeAt(pos)))pos++;
208
- }
209
- if (pos < len && 0x2e === text.charCodeAt(pos)) {
210
- pos++;
211
- if (!isDigit(text.charCodeAt(pos))) {
212
- tokenError = "UnexpectedEndOfNumber";
213
- return text.substring(start, pos);
214
- }
215
- pos++;
216
- while(pos < len && isDigit(text.charCodeAt(pos)))pos++;
217
- }
218
- if (pos < len && (0x45 === text.charCodeAt(pos) || 0x65 === text.charCodeAt(pos))) {
219
- pos++;
220
- if (pos < len && (0x2b === text.charCodeAt(pos) || 0x2d === text.charCodeAt(pos))) pos++;
221
- if (!isDigit(text.charCodeAt(pos))) {
222
- tokenError = "UnexpectedEndOfNumber";
223
- return text.substring(start, pos);
224
- }
225
- pos++;
226
- while(pos < len && isDigit(text.charCodeAt(pos)))pos++;
227
- }
228
- return text.substring(start, pos);
229
- };
230
- const scan = ()=>{
231
- tokenValue = "";
232
- tokenError = "None";
233
- if (pos >= len) {
234
- tokenOffset = len;
235
- tokenStartLine = lineNumber;
236
- tokenStartCharacter = pos - lineStartOffset;
237
- token = "EOF";
238
- return token;
239
- }
240
- let ch = text.charCodeAt(pos);
241
- if (isWhitespace(ch)) {
242
- tokenOffset = pos;
243
- tokenStartLine = lineNumber;
244
- tokenStartCharacter = pos - lineStartOffset;
245
- do {
246
- pos++;
247
- ch = pos < len ? text.charCodeAt(pos) : 0;
248
- }while (isWhitespace(ch));
249
- tokenValue = text.substring(tokenOffset, pos);
250
- if (ignoreTrivia) return scan();
251
- token = "Trivia";
252
- return token;
253
- }
254
- if (isLineBreak(ch)) {
255
- tokenOffset = pos;
256
- tokenStartLine = lineNumber;
257
- tokenStartCharacter = pos - lineStartOffset;
258
- pos++;
259
- if (0x0d === ch && pos < len && 0x0a === text.charCodeAt(pos)) pos++;
260
- lineNumber++;
261
- lineStartOffset = pos;
262
- tokenValue = text.substring(tokenOffset, pos);
263
- if (ignoreTrivia) return scan();
264
- token = "LineBreak";
265
- return token;
266
- }
267
- tokenOffset = pos;
268
- tokenStartLine = lineNumber;
269
- tokenStartCharacter = pos - lineStartOffset;
270
- switch(ch){
271
- case 0x7b:
272
- pos++;
273
- tokenValue = "{";
274
- token = "OpenBrace";
275
- return token;
276
- case 0x7d:
277
- pos++;
278
- tokenValue = "}";
279
- token = "CloseBrace";
280
- return token;
281
- case 0x5b:
282
- pos++;
283
- tokenValue = "[";
284
- token = "OpenBracket";
285
- return token;
286
- case 0x5d:
287
- pos++;
288
- tokenValue = "]";
289
- token = "CloseBracket";
290
- return token;
291
- case 0x3a:
292
- pos++;
293
- tokenValue = ":";
294
- token = "Colon";
295
- return token;
296
- case 0x2c:
297
- pos++;
298
- tokenValue = ",";
299
- token = "Comma";
300
- return token;
301
- case 0x22:
302
- tokenValue = scanString();
303
- token = "String";
304
- return token;
305
- case 0x2f:
306
- {
307
- const nextCh = pos + 1 < len ? text.charCodeAt(pos + 1) : 0;
308
- if (0x2f === nextCh) {
309
- pos += 2;
310
- while(pos < len && !isLineBreak(text.charCodeAt(pos)))pos++;
311
- tokenValue = text.substring(tokenOffset, pos);
312
- if (ignoreTrivia) return scan();
313
- token = "LineComment";
314
- return token;
315
- }
316
- if (0x2a === nextCh) {
317
- pos += 2;
318
- const safeLen = len - 1;
319
- let commentClosed = false;
320
- while(pos < safeLen){
321
- const cch = text.charCodeAt(pos);
322
- if (isLineBreak(cch)) {
323
- if (0x0d === cch && pos + 1 < len && 0x0a === text.charCodeAt(pos + 1)) pos++;
324
- pos++;
325
- lineNumber++;
326
- lineStartOffset = pos;
327
- } else if (0x2a === cch && 0x2f === text.charCodeAt(pos + 1)) {
328
- pos += 2;
329
- commentClosed = true;
330
- break;
331
- } else pos++;
332
- }
333
- if (!commentClosed) {
334
- pos = len;
335
- tokenError = "UnexpectedEndOfComment";
336
- }
337
- tokenValue = text.substring(tokenOffset, pos);
338
- if (ignoreTrivia) return scan();
339
- token = "BlockComment";
340
- return token;
341
- }
342
- pos++;
343
- tokenValue = text.substring(tokenOffset, pos);
344
- token = "Unknown";
345
- tokenError = "InvalidCharacter";
346
- return token;
347
- }
348
- case 0x2d:
349
- if (pos + 1 < len && isDigit(text.charCodeAt(pos + 1))) {
350
- tokenValue = scanNumber();
351
- token = "Number";
352
- return token;
353
- }
354
- pos++;
355
- tokenValue = "-";
356
- token = "Unknown";
357
- tokenError = "InvalidSymbol";
358
- return token;
359
- default:
360
- if (isDigit(ch)) {
361
- tokenValue = scanNumber();
362
- token = "Number";
363
- return token;
364
- }
365
- if (ch >= 0x61 && ch <= 0x7a) {
366
- const start = pos;
367
- pos++;
368
- while(pos < len){
369
- const kch = text.charCodeAt(pos);
370
- if (kch >= 0x61 && kch <= 0x7a) pos++;
371
- else break;
372
- }
373
- tokenValue = text.substring(start, pos);
374
- switch(tokenValue){
375
- case "true":
376
- token = "True";
377
- return token;
378
- case "false":
379
- token = "False";
380
- return token;
381
- case "null":
382
- token = "Null";
383
- return token;
384
- default:
385
- token = "Unknown";
386
- tokenError = "InvalidSymbol";
387
- return token;
388
- }
389
- }
390
- pos++;
391
- tokenValue = text.substring(tokenOffset, pos);
392
- token = "Unknown";
393
- tokenError = "InvalidCharacter";
394
- return token;
395
- }
396
- };
397
- return {
398
- scan,
399
- getToken: ()=>token,
400
- getTokenValue: ()=>tokenValue,
401
- getTokenOffset: ()=>tokenOffset,
402
- getTokenLength: ()=>pos - tokenOffset,
403
- getTokenStartLine: ()=>tokenStartLine,
404
- getTokenStartCharacter: ()=>tokenStartCharacter,
405
- getTokenError: ()=>tokenError,
406
- getPosition: ()=>pos,
407
- setPosition: (newPos)=>{
408
- pos = newPos;
409
- tokenValue = "";
410
- token = "Unknown";
411
- tokenError = "None";
412
- }
413
- };
414
- };
415
- const parse = (text, options)=>Effect.sync(()=>parseInternal(text, options ?? {}, false)).pipe(Effect.flatMap(({ value, errors })=>{
416
- if (errors.length > 0) return Effect.fail(new JsoncParseError({
417
- errors,
418
- text,
419
- ...void 0 !== options ? {
420
- options
421
- } : {}
422
- }));
423
- return Effect.succeed(value);
424
- }));
425
- const parseTree = (text, options)=>Effect.sync(()=>parseInternal(text, options ?? {}, true)).pipe(Effect.flatMap(({ root, errors })=>{
426
- if (errors.length > 0) return Effect.fail(new JsoncParseError({
427
- errors,
428
- text,
429
- ...void 0 !== options ? {
430
- options
431
- } : {}
432
- }));
433
- return Effect.succeed(root ? Option.some(root) : Option.none());
434
- }));
435
- const stripComments = (text, replaceCh)=>Effect.sync(()=>{
436
- const scanner = createScanner(text);
437
- const parts = [];
438
- let lastOffset = 0;
439
- let kind;
440
- do {
441
- kind = scanner.scan();
442
- const offset = scanner.getTokenOffset();
443
- const length = scanner.getTokenLength();
444
- if ("LineComment" === kind || "BlockComment" === kind) {
445
- if (lastOffset < offset) parts.push(text.substring(lastOffset, offset));
446
- if (void 0 !== replaceCh) for(let i = 0; i < length; i++){
447
- const ch = text.charCodeAt(offset + i);
448
- parts.push(0x0a === ch || 0x0d === ch ? text[offset + i] : replaceCh);
449
- }
450
- lastOffset = offset + length;
451
- }
452
- }while ("EOF" !== kind);
453
- if (lastOffset < text.length) parts.push(text.substring(lastOffset));
454
- return parts.join("");
455
- });
456
- function parseInternal(text, options, buildTree) {
457
- const scanner = createScanner(text, false);
458
- const errors = [];
459
- const disallowComments = options.disallowComments ?? false;
460
- const allowTrailingComma = options.allowTrailingComma ?? true;
461
- const allowEmptyContent = options.allowEmptyContent ?? false;
462
- let currentToken = "Unknown";
463
- function token() {
464
- return currentToken;
465
- }
466
- function scanNext() {
467
- for(;;){
468
- currentToken = scanner.scan();
469
- switch(scanner.getTokenError()){
470
- case "InvalidUnicode":
471
- handleError("InvalidUnicode");
472
- break;
473
- case "InvalidEscapeCharacter":
474
- handleError("InvalidEscapeCharacter");
475
- break;
476
- case "UnexpectedEndOfNumber":
477
- handleError("InvalidNumberFormat");
478
- break;
479
- case "UnexpectedEndOfComment":
480
- handleError("UnexpectedEndOfComment");
481
- break;
482
- case "UnexpectedEndOfString":
483
- handleError("UnexpectedEndOfString");
484
- break;
485
- case "InvalidCharacter":
486
- handleError("InvalidCharacter");
487
- break;
488
- }
489
- switch(currentToken){
490
- case "LineComment":
491
- case "BlockComment":
492
- if (disallowComments) handleError("InvalidCommentToken");
493
- break;
494
- case "Trivia":
495
- case "LineBreak":
496
- break;
497
- default:
498
- return currentToken;
499
- }
500
- }
501
- }
502
- function handleError(code, skipUntilAfter = [], skipUntil = []) {
503
- errors.push(new JsoncParseErrorDetail({
504
- code,
505
- message: formatError(code, scanner.getTokenOffset()),
506
- offset: scanner.getTokenOffset(),
507
- length: scanner.getTokenLength(),
508
- startLine: scanner.getTokenStartLine(),
509
- startCharacter: scanner.getTokenStartCharacter()
510
- }));
511
- if (skipUntilAfter.length > 0 || skipUntil.length > 0) {
512
- let t = token();
513
- while("EOF" !== t){
514
- if (skipUntilAfter.includes(t)) {
515
- scanNext();
516
- break;
517
- }
518
- if (skipUntil.includes(t)) break;
519
- t = scanNext();
520
- }
521
- }
522
- }
523
- function parseValue() {
524
- switch(token()){
525
- case "OpenBracket":
526
- return parseArray();
527
- case "OpenBrace":
528
- return parseObject();
529
- case "String":
530
- return parseString();
531
- case "Number":
532
- return parseNumber();
533
- case "True":
534
- scanNext();
535
- return true;
536
- case "False":
537
- scanNext();
538
- return false;
539
- case "Null":
540
- scanNext();
541
- return null;
542
- default:
543
- return;
544
- }
545
- }
546
- function parseString() {
547
- const value = scanner.getTokenValue();
548
- scanNext();
549
- return value;
550
- }
551
- function parseNumber() {
552
- const value = Number.parseFloat(scanner.getTokenValue());
553
- scanNext();
554
- return value;
555
- }
556
- function parseArray() {
557
- scanNext();
558
- const arr = [];
559
- let needsComma = false;
560
- while("CloseBracket" !== token() && "EOF" !== token()){
561
- if ("Comma" === token()) {
562
- if (!needsComma) handleError("ValueExpected");
563
- scanNext();
564
- if ("CloseBracket" === token() && allowTrailingComma) break;
565
- } else if (needsComma) handleError("CommaExpected");
566
- const value = parseValue();
567
- if (void 0 === value) handleError("ValueExpected", [], [
568
- "CloseBracket",
569
- "Comma"
570
- ]);
571
- else arr.push(value);
572
- needsComma = true;
573
- }
574
- if ("CloseBracket" !== token()) handleError("CloseBracketExpected");
575
- else scanNext();
576
- return arr;
577
- }
578
- function parseObject() {
579
- scanNext();
580
- const obj = {};
581
- let needsComma = false;
582
- while("CloseBrace" !== token() && "EOF" !== token()){
583
- if ("Comma" === token()) {
584
- if (!needsComma) handleError("PropertyNameExpected");
585
- scanNext();
586
- if ("CloseBrace" === token() && allowTrailingComma) break;
587
- } else if (needsComma) handleError("CommaExpected");
588
- if ("String" !== token()) {
589
- handleError("PropertyNameExpected", [], [
590
- "CloseBrace",
591
- "Comma"
592
- ]);
593
- continue;
594
- }
595
- const key = scanner.getTokenValue();
596
- scanNext();
597
- if ("Colon" !== token()) {
598
- handleError("ColonExpected", [], [
599
- "CloseBrace",
600
- "Comma"
601
- ]);
602
- continue;
603
- }
604
- scanNext();
605
- const value = parseValue();
606
- if (void 0 === value) handleError("ValueExpected", [], [
607
- "CloseBrace",
608
- "Comma"
609
- ]);
610
- else obj[key] = value;
611
- needsComma = true;
612
- }
613
- if ("CloseBrace" !== token()) handleError("CloseBraceExpected");
614
- else scanNext();
615
- return obj;
616
- }
617
- function parseValueTree() {
618
- switch(token()){
619
- case "OpenBracket":
620
- return parseArrayTree();
621
- case "OpenBrace":
622
- return parseObjectTree();
623
- case "String":
624
- {
625
- const node = {
626
- type: "string",
627
- offset: scanner.getTokenOffset(),
628
- length: 0,
629
- value: scanner.getTokenValue()
630
- };
631
- scanNext();
632
- node.length = scanner.getTokenOffset() - node.offset;
633
- return node;
634
- }
635
- case "Number":
636
- {
637
- const node = {
638
- type: "number",
639
- offset: scanner.getTokenOffset(),
640
- length: 0,
641
- value: Number.parseFloat(scanner.getTokenValue())
642
- };
643
- scanNext();
644
- node.length = scanner.getTokenOffset() - node.offset;
645
- return node;
646
- }
647
- case "True":
648
- {
649
- const node = {
650
- type: "boolean",
651
- offset: scanner.getTokenOffset(),
652
- length: 0,
653
- value: true
654
- };
655
- scanNext();
656
- node.length = scanner.getTokenOffset() - node.offset;
657
- return node;
658
- }
659
- case "False":
660
- {
661
- const node = {
662
- type: "boolean",
663
- offset: scanner.getTokenOffset(),
664
- length: 0,
665
- value: false
666
- };
667
- scanNext();
668
- node.length = scanner.getTokenOffset() - node.offset;
669
- return node;
670
- }
671
- case "Null":
672
- {
673
- const node = {
674
- type: "null",
675
- offset: scanner.getTokenOffset(),
676
- length: 0,
677
- value: null
678
- };
679
- scanNext();
680
- node.length = scanner.getTokenOffset() - node.offset;
681
- return node;
682
- }
683
- default:
684
- return;
685
- }
686
- }
687
- function parseArrayTree() {
688
- const node = {
689
- type: "array",
690
- offset: scanner.getTokenOffset(),
691
- length: 0,
692
- children: []
693
- };
694
- scanNext();
695
- let needsComma = false;
696
- while("CloseBracket" !== token() && "EOF" !== token()){
697
- if ("Comma" === token()) {
698
- if (!needsComma) handleError("ValueExpected");
699
- scanNext();
700
- if ("CloseBracket" === token() && allowTrailingComma) break;
701
- } else if (needsComma) handleError("CommaExpected");
702
- const child = parseValueTree();
703
- if (child) node.children.push(child);
704
- else handleError("ValueExpected", [], [
705
- "CloseBracket",
706
- "Comma"
707
- ]);
708
- needsComma = true;
709
- }
710
- if ("CloseBracket" !== token()) handleError("CloseBracketExpected");
711
- else scanNext();
712
- node.length = scanner.getTokenOffset() - node.offset;
713
- return node;
714
- }
715
- function parseObjectTree() {
716
- const node = {
717
- type: "object",
718
- offset: scanner.getTokenOffset(),
719
- length: 0,
720
- children: []
721
- };
722
- scanNext();
723
- let needsComma = false;
724
- while("CloseBrace" !== token() && "EOF" !== token()){
725
- if ("Comma" === token()) {
726
- if (!needsComma) handleError("PropertyNameExpected");
727
- scanNext();
728
- if ("CloseBrace" === token() && allowTrailingComma) break;
729
- } else if (needsComma) handleError("CommaExpected");
730
- if ("String" !== token()) {
731
- handleError("PropertyNameExpected", [], [
732
- "CloseBrace",
733
- "Comma"
734
- ]);
735
- continue;
736
- }
737
- const property = {
738
- type: "property",
739
- offset: scanner.getTokenOffset(),
740
- length: 0,
741
- children: []
742
- };
743
- const keyNode = {
744
- type: "string",
745
- offset: scanner.getTokenOffset(),
746
- length: 0,
747
- value: scanner.getTokenValue()
748
- };
749
- scanNext();
750
- keyNode.length = scanner.getTokenOffset() - keyNode.offset;
751
- property.children.push(keyNode);
752
- if ("Colon" !== token()) {
753
- handleError("ColonExpected", [], [
754
- "CloseBrace",
755
- "Comma"
756
- ]);
757
- property.length = scanner.getTokenOffset() - property.offset;
758
- node.children.push(property);
759
- continue;
760
- }
761
- property.colonOffset = scanner.getTokenOffset();
762
- scanNext();
763
- const valueNode = parseValueTree();
764
- if (valueNode) property.children.push(valueNode);
765
- else handleError("ValueExpected", [], [
766
- "CloseBrace",
767
- "Comma"
768
- ]);
769
- property.length = scanner.getTokenOffset() - property.offset;
770
- node.children.push(property);
771
- needsComma = true;
772
- }
773
- if ("CloseBrace" !== token()) handleError("CloseBraceExpected");
774
- else scanNext();
775
- node.length = scanner.getTokenOffset() - node.offset;
776
- return node;
777
- }
778
- scanNext();
779
- if (buildTree) {
780
- const root = parseValueTree();
781
- if ("EOF" !== token()) handleError("EndOfFileExpected");
782
- if (!root && !allowEmptyContent) handleError("ValueExpected");
783
- return {
784
- value: void 0,
785
- root,
786
- errors
787
- };
788
- }
789
- const value = parseValue();
790
- if ("EOF" !== token()) handleError("EndOfFileExpected");
791
- if (void 0 === value && !allowEmptyContent) handleError("ValueExpected");
792
- return {
793
- value,
794
- root: void 0,
795
- errors
796
- };
797
- }
798
- function formatError(code, offset) {
799
- switch(code){
800
- case "InvalidSymbol":
801
- return `Invalid symbol at offset ${offset}`;
802
- case "InvalidNumberFormat":
803
- return `Invalid number format at offset ${offset}`;
804
- case "PropertyNameExpected":
805
- return `Property name expected at offset ${offset}`;
806
- case "ValueExpected":
807
- return `Value expected at offset ${offset}`;
808
- case "ColonExpected":
809
- return `Colon expected at offset ${offset}`;
810
- case "CommaExpected":
811
- return `Comma expected at offset ${offset}`;
812
- case "CloseBraceExpected":
813
- return `Close brace expected at offset ${offset}`;
814
- case "CloseBracketExpected":
815
- return `Close bracket expected at offset ${offset}`;
816
- case "EndOfFileExpected":
817
- return `End of file expected at offset ${offset}`;
818
- case "InvalidCommentToken":
819
- return `Comments not allowed at offset ${offset}`;
820
- case "UnexpectedEndOfComment":
821
- return `Unexpected end of comment at offset ${offset}`;
822
- case "UnexpectedEndOfString":
823
- return `Unexpected end of string at offset ${offset}`;
824
- case "UnexpectedEndOfNumber":
825
- return `Unexpected end of number at offset ${offset}`;
826
- case "InvalidUnicode":
827
- return `Invalid unicode escape at offset ${offset}`;
828
- case "InvalidEscapeCharacter":
829
- return `Invalid escape character at offset ${offset}`;
830
- case "InvalidCharacter":
831
- return `Invalid character at offset ${offset}`;
832
- default:
833
- return `Parse error at offset ${offset}`;
834
- }
835
- }
836
- function deepEqual(a, b) {
837
- if (a === b) return true;
838
- if (null === a || null === b) return false;
839
- if (typeof a !== typeof b) return false;
840
- if (Array.isArray(a)) {
841
- if (!Array.isArray(b)) return false;
842
- if (a.length !== b.length) return false;
843
- for(let i = 0; i < a.length; i++)if (!deepEqual(a[i], b[i])) return false;
844
- return true;
845
- }
846
- if (Array.isArray(b)) return false;
847
- if ("object" == typeof a && "object" == typeof b) {
848
- const aObj = a;
849
- const bObj = b;
850
- const aKeys = Object.keys(aObj);
851
- const bKeys = Object.keys(bObj);
852
- if (aKeys.length !== bKeys.length) return false;
853
- for (const key of aKeys){
854
- if (!Object.hasOwn(bObj, key)) return false;
855
- if (!deepEqual(aObj[key], bObj[key])) return false;
856
- }
857
- return true;
858
- }
859
- return false;
860
- }
861
- const equals = Function.dual(2, (self, that)=>Effect.map(Effect.all([
862
- parse(self),
863
- parse(that)
864
- ]), ([a, b])=>deepEqual(a, b)));
865
- const equalsValue = Function.dual(2, (self, value)=>Effect.map(parse(self), (parsed)=>deepEqual(parsed, value)));
866
- const format = (text, range, options)=>Effect.sync(()=>formatImpl(text, range, options));
867
- const applyEdits = Function.dual(2, (text, edits)=>Effect.sync(()=>{
868
- const sorted = [
869
- ...edits
870
- ].sort((a, b)=>b.offset - a.offset);
871
- let result = text;
872
- for (const edit of sorted)result = result.substring(0, edit.offset) + edit.content + result.substring(edit.offset + edit.length);
873
- return result;
874
- }));
875
- const formatAndApply = (text, range, options)=>format(text, range, options).pipe(Effect.flatMap((edits)=>applyEdits(text, edits)));
876
- const modify = Function.dual((args)=>"string" == typeof args[0] && Array.isArray(args[1]), (text, path, value, options)=>Effect["try"]({
877
- try: ()=>modifyImpl(text, path, value, options?.formattingOptions),
878
- catch: (e)=>new JsoncModificationError({
879
- path,
880
- reason: String(e)
881
- })
882
- }));
883
- function formatImpl(text, range, options) {
884
- const opts = {
885
- tabSize: options?.tabSize ?? 2,
886
- insertSpaces: options?.insertSpaces ?? true,
887
- eol: options?.eol ?? "\n",
888
- insertFinalNewline: options?.insertFinalNewline ?? false,
889
- keepLines: options?.keepLines ?? false
890
- };
891
- const indentUnit = opts.insertSpaces ? " ".repeat(opts.tabSize) : "\t";
892
- const edits = [];
893
- const scanner = createScanner(text, false);
894
- const rangeStart = range?.offset ?? 0;
895
- const rangeEnd = range ? range.offset + range.length : text.length;
896
- let depth = 0;
897
- let prevTokenEnd = -1;
898
- let prevToken = "Unknown";
899
- let firstToken = true;
900
- function makeIndent(d) {
901
- return indentUnit.repeat(d);
902
- }
903
- function addEdit(offset, length, content) {
904
- if (offset >= rangeStart && offset + length <= rangeEnd) {
905
- if (text.substring(offset, offset + length) !== content) edits.push({
906
- offset,
907
- length,
908
- content
909
- });
910
- }
911
- }
912
- let kind = scanner.scan();
913
- while("EOF" !== kind){
914
- const tokenOffset = scanner.getTokenOffset();
915
- const tokenLength = scanner.getTokenLength();
916
- if ("Trivia" !== kind && "LineBreak" !== kind) {
917
- if (!firstToken && prevTokenEnd >= 0) {
918
- const gap = text.substring(prevTokenEnd, tokenOffset);
919
- let expectedGap;
920
- if ("CloseBrace" === kind || "CloseBracket" === kind) {
921
- depth--;
922
- expectedGap = opts.eol + makeIndent(depth);
923
- } else expectedGap = "OpenBrace" === prevToken || "OpenBracket" === prevToken ? opts.eol + makeIndent(depth) : "Comma" === prevToken ? opts.eol + makeIndent(depth) : "Colon" === prevToken ? " " : "LineComment" === kind || "BlockComment" === kind ? gap.includes("\n") ? opts.eol + makeIndent(depth) : " " : "LineComment" === prevToken ? opts.eol + makeIndent(depth) : "BlockComment" === prevToken ? gap.includes("\n") ? opts.eol + makeIndent(depth) : " " : gap;
924
- if (opts.keepLines && gap.includes("\n")) expectedGap = gap;
925
- addEdit(prevTokenEnd, tokenOffset - prevTokenEnd, expectedGap);
926
- }
927
- if ("OpenBrace" === kind || "OpenBracket" === kind) depth++;
928
- prevToken = kind;
929
- prevTokenEnd = tokenOffset + tokenLength;
930
- firstToken = false;
931
- }
932
- kind = scanner.scan();
933
- }
934
- if (opts.insertFinalNewline && prevTokenEnd >= 0) {
935
- const trailing = text.substring(prevTokenEnd);
936
- if (!trailing.endsWith(opts.eol)) edits.push({
937
- offset: prevTokenEnd,
938
- length: trailing.length,
939
- content: opts.eol
940
- });
941
- }
942
- return edits;
943
- }
944
- function modifyImpl(text, path, value, formattingOptions) {
945
- const opts = {
946
- tabSize: formattingOptions?.tabSize ?? 2,
947
- insertSpaces: formattingOptions?.insertSpaces ?? true,
948
- eol: formattingOptions?.eol ?? "\n"
949
- };
950
- const indentUnit = opts.insertSpaces ? " ".repeat(opts.tabSize) : "\t";
951
- if (0 === path.length) {
952
- const content = void 0 === value ? "" : JSON.stringify(value, null, opts.tabSize);
953
- return [
954
- {
955
- offset: 0,
956
- length: text.length,
957
- content
958
- }
959
- ];
960
- }
961
- const scanner = createScanner(text, true);
962
- let currentToken = scanner.scan();
963
- function skipValue() {
964
- switch(currentToken){
965
- case "OpenBrace":
966
- {
967
- currentToken = scanner.scan();
968
- let first = true;
969
- while("CloseBrace" !== currentToken && "EOF" !== currentToken){
970
- if (!first && "Comma" === currentToken) currentToken = scanner.scan();
971
- if ("String" === currentToken) {
972
- currentToken = scanner.scan();
973
- if ("Colon" === currentToken) {
974
- currentToken = scanner.scan();
975
- skipValue();
976
- }
977
- } else currentToken = scanner.scan();
978
- first = false;
979
- }
980
- if ("CloseBrace" === currentToken) currentToken = scanner.scan();
981
- break;
982
- }
983
- case "OpenBracket":
984
- {
985
- currentToken = scanner.scan();
986
- let first = true;
987
- while("CloseBracket" !== currentToken && "EOF" !== currentToken){
988
- if (!first && "Comma" === currentToken) currentToken = scanner.scan();
989
- skipValue();
990
- first = false;
991
- }
992
- if ("CloseBracket" === currentToken) currentToken = scanner.scan();
993
- break;
994
- }
995
- default:
996
- currentToken = scanner.scan();
997
- break;
998
- }
999
- }
1000
- let depth = 0;
1001
- for (const segment of path){
1002
- depth++;
1003
- if ("string" == typeof segment) {
1004
- if ("OpenBrace" !== currentToken) throw new Error(`Expected object at depth ${depth}`);
1005
- currentToken = scanner.scan();
1006
- let found = false;
1007
- let lastValueEnd = scanner.getTokenOffset();
1008
- let isFirst = true;
1009
- while("CloseBrace" !== currentToken && "EOF" !== currentToken){
1010
- if (!isFirst && "Comma" === currentToken) currentToken = scanner.scan();
1011
- if ("String" === currentToken) {
1012
- const key = scanner.getTokenValue();
1013
- currentToken = scanner.scan();
1014
- if ("Colon" === currentToken) currentToken = scanner.scan();
1015
- if (key === segment) {
1016
- found = true;
1017
- if (depth === path.length) {
1018
- const valueStart = scanner.getTokenOffset();
1019
- const prevEnd = valueStart;
1020
- skipValue();
1021
- const valueEnd = scanner.getTokenOffset();
1022
- if (void 0 === value) {
1023
- let removeStart = valueStart;
1024
- let removeEnd = valueEnd;
1025
- const keySearchArea = text.substring(0, valueStart);
1026
- const keyStart = keySearchArea.lastIndexOf(`"${segment}"`);
1027
- if (keyStart >= 0) removeStart = keyStart;
1028
- const beforeKey = text.substring(0, removeStart).trimEnd();
1029
- const commaPosBefore = beforeKey.lastIndexOf(",");
1030
- if (commaPosBefore >= 0) removeStart = commaPosBefore;
1031
- else {
1032
- const afterValue = text.substring(removeEnd);
1033
- const trimmedAfter = afterValue.match(/^(\s*,)/);
1034
- if (trimmedAfter) removeEnd += trimmedAfter[1].length;
1035
- }
1036
- return [
1037
- {
1038
- offset: removeStart,
1039
- length: removeEnd - removeStart,
1040
- content: ""
1041
- }
1042
- ];
1043
- }
1044
- const serialized = JSON.stringify(value, null, opts.tabSize);
1045
- return [
1046
- {
1047
- offset: prevEnd,
1048
- length: valueEnd - prevEnd,
1049
- content: serialized
1050
- }
1051
- ];
1052
- }
1053
- break;
1054
- }
1055
- skipValue();
1056
- } else currentToken = scanner.scan();
1057
- lastValueEnd = scanner.getTokenOffset();
1058
- isFirst = false;
1059
- }
1060
- if (!found && depth === path.length && void 0 !== value) {
1061
- const indent = indentUnit.repeat(depth);
1062
- const serialized = JSON.stringify(value, null, opts.tabSize);
1063
- const insertText = isFirst ? `${opts.eol}${indent}"${segment}": ${serialized}${opts.eol}${indentUnit.repeat(depth - 1)}` : `,${opts.eol}${indent}"${segment}": ${serialized}`;
1064
- return [
1065
- {
1066
- offset: lastValueEnd,
1067
- length: 0,
1068
- content: insertText
1069
- }
1070
- ];
1071
- }
1072
- } else {
1073
- if ("OpenBracket" !== currentToken) throw new Error(`Expected array at depth ${depth}`);
1074
- currentToken = scanner.scan();
1075
- let idx = 0;
1076
- let lastEnd = scanner.getTokenOffset();
1077
- while("CloseBracket" !== currentToken && "EOF" !== currentToken){
1078
- if (idx > 0 && "Comma" === currentToken) currentToken = scanner.scan();
1079
- if (idx === segment) {
1080
- if (depth === path.length) {
1081
- const valueStart = scanner.getTokenOffset();
1082
- skipValue();
1083
- const valueEnd = scanner.getTokenOffset();
1084
- if (void 0 === value) {
1085
- let removeEnd = valueEnd;
1086
- const after = text.substring(removeEnd).trimStart();
1087
- if (after.startsWith(",")) removeEnd = text.indexOf(",", removeEnd) + 1;
1088
- return [
1089
- {
1090
- offset: valueStart,
1091
- length: removeEnd - valueStart,
1092
- content: ""
1093
- }
1094
- ];
1095
- }
1096
- const serialized = JSON.stringify(value, null, opts.tabSize);
1097
- return [
1098
- {
1099
- offset: valueStart,
1100
- length: valueEnd - valueStart,
1101
- content: serialized
1102
- }
1103
- ];
1104
- }
1105
- break;
1106
- }
1107
- skipValue();
1108
- lastEnd = scanner.getTokenOffset();
1109
- idx++;
1110
- }
1111
- if (idx <= segment && depth === path.length && void 0 !== value) {
1112
- const indent = indentUnit.repeat(depth);
1113
- const serialized = JSON.stringify(value, null, opts.tabSize);
1114
- const insertText = 0 === idx ? `${opts.eol}${indent}${serialized}${opts.eol}${indentUnit.repeat(depth - 1)}` : `,${opts.eol}${indent}${serialized}`;
1115
- return [
1116
- {
1117
- offset: lastEnd,
1118
- length: 0,
1119
- content: insertText
1120
- }
1121
- ];
1122
- }
1123
- }
1124
- }
1125
- return [];
1126
- }
1127
- const JsoncFromString = makeJsoncFromString();
1128
- function makeJsoncFromString(options) {
1129
- return Schema.transformOrFail(Schema.String, Schema.Unknown, {
1130
- strict: true,
1131
- decode: (input, _options, ast)=>{
1132
- const program = options ? parse(input, options) : parse(input);
1133
- return Effect.mapError(program, (parseError)=>new ParseResult.Type(ast, input, parseError.message));
1134
- },
1135
- encode: (value)=>ParseResult.succeed(JSON.stringify(value, null, 2))
1136
- }).annotations({
1137
- title: "JsoncFromString",
1138
- description: "Parse a JSONC string into an unknown JavaScript value"
1139
- });
1140
- }
1141
- const makeJsoncSchema = (targetSchema, options)=>Schema.compose(makeJsoncFromString(options), targetSchema);
1142
- const JsoncSyntaxKind = Schema.Literal("OpenBrace", "CloseBrace", "OpenBracket", "CloseBracket", "Comma", "Colon", "Null", "True", "False", "String", "Number", "LineComment", "BlockComment", "LineBreak", "Trivia", "Unknown", "EOF");
1143
- const JsoncScanError = Schema.Literal("None", "UnexpectedEndOfComment", "UnexpectedEndOfString", "UnexpectedEndOfNumber", "InvalidUnicode", "InvalidEscapeCharacter", "InvalidCharacter", "InvalidSymbol");
1144
- class JsoncToken extends Schema.Class("JsoncToken")({
1145
- kind: JsoncSyntaxKind,
1146
- value: Schema.String,
1147
- offset: Schema.Number,
1148
- length: Schema.Number,
1149
- startLine: Schema.Number,
1150
- startCharacter: Schema.Number,
1151
- error: JsoncScanError
1152
- }) {
1153
- }
1154
- const JsoncNodeType = Schema.Literal("object", "array", "property", "string", "number", "boolean", "null");
1155
- class JsoncNode extends Schema.Class("JsoncNode")({
1156
- type: JsoncNodeType,
1157
- value: Schema.optional(Schema.Unknown),
1158
- offset: Schema.Number,
1159
- length: Schema.Number,
1160
- colonOffset: Schema.optional(Schema.Number),
1161
- children: Schema.optional(Schema.Array(Schema.suspend(()=>JsoncNode)))
1162
- }) {
1163
- }
1164
- const JsoncSegment = Schema.Union(Schema.String, Schema.Number);
1165
- Schema.Array(JsoncSegment);
1166
- class JsoncEdit extends Schema.Class("JsoncEdit")({
1167
- offset: Schema.Number,
1168
- length: Schema.Number,
1169
- content: Schema.String
1170
- }) {
1171
- }
1172
- class JsoncRange extends Schema.Class("JsoncRange")({
1173
- offset: Schema.Number,
1174
- length: Schema.Number
1175
- }) {
1176
- }
1177
- class JsoncParseOptions extends Schema.Class("JsoncParseOptions")({
1178
- disallowComments: Schema.optionalWith(Schema.Boolean, {
1179
- default: ()=>false
1180
- }),
1181
- allowTrailingComma: Schema.optionalWith(Schema.Boolean, {
1182
- default: ()=>true
1183
- }),
1184
- allowEmptyContent: Schema.optionalWith(Schema.Boolean, {
1185
- default: ()=>false
1186
- })
1187
- }) {
1188
- }
1189
- class JsoncFormattingOptions extends Schema.Class("JsoncFormattingOptions")({
1190
- tabSize: Schema.optionalWith(Schema.Number, {
1191
- default: ()=>2
1192
- }),
1193
- insertSpaces: Schema.optionalWith(Schema.Boolean, {
1194
- default: ()=>true
1195
- }),
1196
- eol: Schema.optionalWith(Schema.String, {
1197
- default: ()=>"\n"
1198
- }),
1199
- insertFinalNewline: Schema.optionalWith(Schema.Boolean, {
1200
- default: ()=>false
1201
- }),
1202
- keepLines: Schema.optionalWith(Schema.Boolean, {
1203
- default: ()=>false
1204
- })
1205
- }) {
1206
- }
1207
- const visit = (text, options)=>Stream.fromIterable(visitGen(text, options));
1208
- const visitCollect = (text, predicate, options)=>visit(text, options).pipe(Stream.filter(predicate), Stream.runCollect, Effect.map(Chunk.toReadonlyArray));
1209
- function* visitGen(text, options) {
1210
- const scanner = createScanner(text, false);
1211
- const disallowComments = options?.disallowComments ?? false;
1212
- const path = [];
1213
- function* scanNext() {
1214
- for(;;){
1215
- const t = scanner.scan();
1216
- const scanError = scanner.getTokenError();
1217
- if ("None" !== scanError) {
1218
- let code;
1219
- switch(scanError){
1220
- case "InvalidUnicode":
1221
- code = "InvalidUnicode";
1222
- break;
1223
- case "InvalidEscapeCharacter":
1224
- code = "InvalidEscapeCharacter";
1225
- break;
1226
- case "UnexpectedEndOfNumber":
1227
- code = "InvalidNumberFormat";
1228
- break;
1229
- case "UnexpectedEndOfComment":
1230
- code = "UnexpectedEndOfComment";
1231
- break;
1232
- case "UnexpectedEndOfString":
1233
- code = "UnexpectedEndOfString";
1234
- break;
1235
- case "InvalidCharacter":
1236
- code = "InvalidCharacter";
1237
- break;
1238
- default:
1239
- code = "InvalidSymbol";
1240
- }
1241
- yield {
1242
- _tag: "Error",
1243
- code,
1244
- offset: scanner.getTokenOffset(),
1245
- length: scanner.getTokenLength()
1246
- };
1247
- }
1248
- switch(t){
1249
- case "LineComment":
1250
- case "BlockComment":
1251
- if (disallowComments) yield {
1252
- _tag: "Error",
1253
- code: "InvalidCommentToken",
1254
- offset: scanner.getTokenOffset(),
1255
- length: scanner.getTokenLength()
1256
- };
1257
- else yield {
1258
- _tag: "Comment",
1259
- offset: scanner.getTokenOffset(),
1260
- length: scanner.getTokenLength()
1261
- };
1262
- break;
1263
- case "Trivia":
1264
- case "LineBreak":
1265
- break;
1266
- default:
1267
- return t;
1268
- }
1269
- }
1270
- }
1271
- function getLiteralValue(kind, tokenValue) {
1272
- switch(kind){
1273
- case "String":
1274
- return tokenValue;
1275
- case "Number":
1276
- return Number.parseFloat(tokenValue);
1277
- case "True":
1278
- return true;
1279
- case "False":
1280
- return false;
1281
- case "Null":
1282
- return null;
1283
- default:
1284
- return;
1285
- }
1286
- }
1287
- function* visitValue() {
1288
- const t = scanner.getToken();
1289
- switch(t){
1290
- case "OpenBrace":
1291
- return yield* visitObject();
1292
- case "OpenBracket":
1293
- return yield* visitArray();
1294
- case "String":
1295
- case "Number":
1296
- case "True":
1297
- case "False":
1298
- case "Null":
1299
- yield {
1300
- _tag: "LiteralValue",
1301
- value: getLiteralValue(t, scanner.getTokenValue()),
1302
- offset: scanner.getTokenOffset(),
1303
- length: scanner.getTokenLength(),
1304
- path: [
1305
- ...path
1306
- ]
1307
- };
1308
- yield* scanNext();
1309
- return true;
1310
- default:
1311
- yield {
1312
- _tag: "Error",
1313
- code: "ValueExpected",
1314
- offset: scanner.getTokenOffset(),
1315
- length: scanner.getTokenLength()
1316
- };
1317
- return false;
1318
- }
1319
- }
1320
- function* visitObject() {
1321
- yield {
1322
- _tag: "ObjectBegin",
1323
- offset: scanner.getTokenOffset(),
1324
- length: scanner.getTokenLength(),
1325
- path: [
1326
- ...path
1327
- ]
1328
- };
1329
- yield* scanNext();
1330
- let needsComma = false;
1331
- while("CloseBrace" !== scanner.getToken() && "EOF" !== scanner.getToken()){
1332
- if ("Comma" === scanner.getToken()) {
1333
- yield {
1334
- _tag: "Separator",
1335
- character: ",",
1336
- offset: scanner.getTokenOffset(),
1337
- length: scanner.getTokenLength()
1338
- };
1339
- yield* scanNext();
1340
- if ("CloseBrace" === scanner.getToken()) break;
1341
- } else if (needsComma) yield {
1342
- _tag: "Error",
1343
- code: "CommaExpected",
1344
- offset: scanner.getTokenOffset(),
1345
- length: scanner.getTokenLength()
1346
- };
1347
- if ("String" !== scanner.getToken()) {
1348
- yield {
1349
- _tag: "Error",
1350
- code: "PropertyNameExpected",
1351
- offset: scanner.getTokenOffset(),
1352
- length: scanner.getTokenLength()
1353
- };
1354
- yield* scanNext();
1355
- continue;
1356
- }
1357
- const key = scanner.getTokenValue();
1358
- yield {
1359
- _tag: "ObjectProperty",
1360
- property: key,
1361
- offset: scanner.getTokenOffset(),
1362
- length: scanner.getTokenLength(),
1363
- path: [
1364
- ...path
1365
- ]
1366
- };
1367
- path.push(key);
1368
- yield* scanNext();
1369
- if ("Colon" === scanner.getToken()) {
1370
- yield {
1371
- _tag: "Separator",
1372
- character: ":",
1373
- offset: scanner.getTokenOffset(),
1374
- length: scanner.getTokenLength()
1375
- };
1376
- yield* scanNext();
1377
- } else yield {
1378
- _tag: "Error",
1379
- code: "ColonExpected",
1380
- offset: scanner.getTokenOffset(),
1381
- length: scanner.getTokenLength()
1382
- };
1383
- yield* visitValue();
1384
- path.pop();
1385
- needsComma = true;
1386
- }
1387
- if ("CloseBrace" === scanner.getToken()) {
1388
- yield {
1389
- _tag: "ObjectEnd",
1390
- offset: scanner.getTokenOffset(),
1391
- length: scanner.getTokenLength()
1392
- };
1393
- yield* scanNext();
1394
- } else yield {
1395
- _tag: "Error",
1396
- code: "CloseBraceExpected",
1397
- offset: scanner.getTokenOffset(),
1398
- length: scanner.getTokenLength()
1399
- };
1400
- return true;
1401
- }
1402
- function* visitArray() {
1403
- yield {
1404
- _tag: "ArrayBegin",
1405
- offset: scanner.getTokenOffset(),
1406
- length: scanner.getTokenLength(),
1407
- path: [
1408
- ...path
1409
- ]
1410
- };
1411
- yield* scanNext();
1412
- let index = 0;
1413
- let needsComma = false;
1414
- while("CloseBracket" !== scanner.getToken() && "EOF" !== scanner.getToken()){
1415
- if ("Comma" === scanner.getToken()) {
1416
- yield {
1417
- _tag: "Separator",
1418
- character: ",",
1419
- offset: scanner.getTokenOffset(),
1420
- length: scanner.getTokenLength()
1421
- };
1422
- yield* scanNext();
1423
- if ("CloseBracket" === scanner.getToken()) break;
1424
- } else if (needsComma) yield {
1425
- _tag: "Error",
1426
- code: "CommaExpected",
1427
- offset: scanner.getTokenOffset(),
1428
- length: scanner.getTokenLength()
1429
- };
1430
- path.push(index);
1431
- yield* visitValue();
1432
- path.pop();
1433
- index++;
1434
- needsComma = true;
1435
- }
1436
- if ("CloseBracket" === scanner.getToken()) {
1437
- yield {
1438
- _tag: "ArrayEnd",
1439
- offset: scanner.getTokenOffset(),
1440
- length: scanner.getTokenLength()
1441
- };
1442
- yield* scanNext();
1443
- } else yield {
1444
- _tag: "Error",
1445
- code: "CloseBracketExpected",
1446
- offset: scanner.getTokenOffset(),
1447
- length: scanner.getTokenLength()
1448
- };
1449
- return true;
1450
- }
1451
- yield* scanNext();
1452
- if ("EOF" !== scanner.getToken()) yield* visitValue();
1453
- }
1454
- export { JsoncEdit, JsoncFormattingOptions, JsoncFromString, JsoncModificationError, JsoncModificationErrorBase, JsoncNode, JsoncNodeNotFoundError, JsoncNodeNotFoundErrorBase, JsoncParseError, JsoncParseErrorBase, JsoncParseErrorDetail, JsoncParseOptions, JsoncRange, JsoncToken, applyEdits, createScanner, equals, equalsValue, findNode, findNodeAtOffset, format, formatAndApply, getNodePath, getNodeValue, makeJsoncFromString, makeJsoncSchema, modify, parse, parseTree, stripComments, visit, visitCollect };
1
+ import { findNode, findNodeAtOffset, getNodePath, getNodeValue } from "./ast.js";
2
+ import { JsoncModificationError, JsoncModificationErrorBase, JsoncNodeNotFoundError, JsoncNodeNotFoundErrorBase, JsoncParseError, JsoncParseErrorBase, JsoncParseErrorDetail } from "./errors.js";
3
+ import { createScanner } from "./scanner.js";
4
+ import { parse, parseTree, stripComments } from "./parse.js";
5
+ import { equals, equalsValue } from "./equality.js";
6
+ import { applyEdits, format, formatAndApply, modify } from "./format.js";
7
+ import { JsoncFromString, makeJsoncFromString, makeJsoncSchema } from "./schema-integration.js";
8
+ import { JsoncEdit, JsoncFormattingOptions, JsoncNode, JsoncParseOptions, JsoncRange, JsoncToken } from "./schemas.js";
9
+ import { visit, visitCollect } from "./visitor.js";
10
+
11
+ export { JsoncEdit, JsoncFormattingOptions, JsoncFromString, JsoncModificationError, JsoncModificationErrorBase, JsoncNode, JsoncNodeNotFoundError, JsoncNodeNotFoundErrorBase, JsoncParseError, JsoncParseErrorBase, JsoncParseErrorDetail, JsoncParseOptions, JsoncRange, JsoncToken, applyEdits, createScanner, equals, equalsValue, findNode, findNodeAtOffset, format, formatAndApply, getNodePath, getNodeValue, makeJsoncFromString, makeJsoncSchema, modify, parse, parseTree, stripComments, visit, visitCollect };