source: trip-planner-front/node_modules/@angular-devkit/core/src/json/parser.js@ 571e0df

Last change on this file since 571e0df was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 25.8 KB
Line 
1"use strict";
2/**
3 * @license
4 * Copyright Google LLC All Rights Reserved.
5 *
6 * Use of this source code is governed by an MIT-style license that can be
7 * found in the LICENSE file at https://angular.io/license
8 */
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.parseJson = exports.parseJsonAst = exports.JsonParseMode = exports.PathSpecificJsonException = exports.UnexpectedEndOfInputException = exports.InvalidJsonCharacterException = exports.JsonException = void 0;
11/* eslint-disable no-constant-condition */
12const exception_1 = require("../exception");
13class JsonException extends exception_1.BaseException {
14}
15exports.JsonException = JsonException;
16/**
17 * A character was invalid in this context.
18 * @deprecated Deprecated since version 11. Use 3rd party JSON parsers such as `jsonc-parser` instead.
19 */
20class InvalidJsonCharacterException extends JsonException {
21 constructor(context) {
22 const pos = context.previous;
23 const invalidChar = JSON.stringify(_peek(context));
24 super(`Invalid JSON character: ${invalidChar} at ${pos.line}:${pos.character}.`);
25 this.invalidChar = invalidChar;
26 this.line = pos.line;
27 this.offset = pos.offset;
28 this.character = pos.character;
29 }
30}
31exports.InvalidJsonCharacterException = InvalidJsonCharacterException;
32/**
33 * More input was expected, but we reached the end of the stream.
34 * @deprecated Deprecated since version 11. Use 3rd party JSON parsers such as `jsonc-parser` instead.
35 */
36class UnexpectedEndOfInputException extends JsonException {
37 constructor(_context) {
38 super(`Unexpected end of file.`);
39 }
40}
41exports.UnexpectedEndOfInputException = UnexpectedEndOfInputException;
42/**
43 * An error happened within a file.
44 * @deprecated Deprecated since version 11. Use 3rd party JSON parsers such as `jsonc-parser` instead.
45 */
46class PathSpecificJsonException extends JsonException {
47 constructor(path, exception) {
48 super(`An error happened at file path ${JSON.stringify(path)}: ${exception.message}`);
49 this.path = path;
50 this.exception = exception;
51 }
52}
53exports.PathSpecificJsonException = PathSpecificJsonException;
54/**
55 * Peek and return the next character from the context.
56 * @private
57 */
58function _peek(context) {
59 return context.original[context.position.offset];
60}
61/**
62 * Move the context to the next character, including incrementing the line if necessary.
63 * @private
64 */
65function _next(context) {
66 context.previous = context.position;
67 let { offset, line, character } = context.position;
68 const char = context.original[offset];
69 offset++;
70 if (char == '\n') {
71 line++;
72 character = 0;
73 }
74 else {
75 character++;
76 }
77 context.position = { offset, line, character };
78}
79function _token(context, valid) {
80 const char = _peek(context);
81 if (valid) {
82 if (!char) {
83 throw new UnexpectedEndOfInputException(context);
84 }
85 if (valid.indexOf(char) == -1) {
86 throw new InvalidJsonCharacterException(context);
87 }
88 }
89 // Move the position of the context to the next character.
90 _next(context);
91 return char;
92}
93/**
94 * Read the exponent part of a number. The exponent part is looser for JSON than the number
95 * part. `str` is the string of the number itself found so far, and start the position
96 * where the full number started. Returns the node found.
97 * @private
98 */
99function _readExpNumber(context, start, str, comments) {
100 let char;
101 let signed = false;
102 while (true) {
103 char = _token(context);
104 if (char == '+' || char == '-') {
105 if (signed) {
106 break;
107 }
108 signed = true;
109 str += char;
110 }
111 else if (char == '0' ||
112 char == '1' ||
113 char == '2' ||
114 char == '3' ||
115 char == '4' ||
116 char == '5' ||
117 char == '6' ||
118 char == '7' ||
119 char == '8' ||
120 char == '9') {
121 signed = true;
122 str += char;
123 }
124 else {
125 break;
126 }
127 }
128 // We're done reading this number.
129 context.position = context.previous;
130 return {
131 kind: 'number',
132 start,
133 end: context.position,
134 text: context.original.substring(start.offset, context.position.offset),
135 value: Number.parseFloat(str),
136 comments: comments,
137 };
138}
139/**
140 * Read the hexa part of a 0xBADCAFE hexadecimal number.
141 * @private
142 */
143function _readHexaNumber(context, isNegative, start, comments) {
144 // Read an hexadecimal number, until it's not hexadecimal.
145 let hexa = '';
146 const valid = '0123456789abcdefABCDEF';
147 for (let ch = _peek(context); ch && valid.includes(ch); ch = _peek(context)) {
148 // Add it to the hexa string.
149 hexa += ch;
150 // Move the position of the context to the next character.
151 _next(context);
152 }
153 const value = Number.parseInt(hexa, 16);
154 // We're done reading this number.
155 return {
156 kind: 'number',
157 start,
158 end: context.position,
159 text: context.original.substring(start.offset, context.position.offset),
160 value: isNegative ? -value : value,
161 comments,
162 };
163}
164/**
165 * Read a number from the context.
166 * @private
167 */
168function _readNumber(context, comments = _readBlanks(context)) {
169 let str = '';
170 let dotted = false;
171 const start = context.position;
172 // read until `e` or end of line.
173 while (true) {
174 const char = _token(context);
175 // Read tokens, one by one.
176 if (char == '-') {
177 if (str != '') {
178 throw new InvalidJsonCharacterException(context);
179 }
180 }
181 else if (char == 'I' &&
182 (str == '-' || str == '' || str == '+') &&
183 (context.mode & JsonParseMode.NumberConstantsAllowed) != 0) {
184 // Infinity?
185 // _token(context, 'I'); Already read.
186 _token(context, 'n');
187 _token(context, 'f');
188 _token(context, 'i');
189 _token(context, 'n');
190 _token(context, 'i');
191 _token(context, 't');
192 _token(context, 'y');
193 str += 'Infinity';
194 break;
195 }
196 else if (char == '0') {
197 if (str == '0' || str == '-0') {
198 throw new InvalidJsonCharacterException(context);
199 }
200 }
201 else if (char == '1' ||
202 char == '2' ||
203 char == '3' ||
204 char == '4' ||
205 char == '5' ||
206 char == '6' ||
207 char == '7' ||
208 char == '8' ||
209 char == '9') {
210 if (str == '0' || str == '-0') {
211 throw new InvalidJsonCharacterException(context);
212 }
213 }
214 else if (char == '+' && str == '') {
215 // Pass over.
216 }
217 else if (char == '.') {
218 if (dotted) {
219 throw new InvalidJsonCharacterException(context);
220 }
221 dotted = true;
222 }
223 else if (char == 'e' || char == 'E') {
224 return _readExpNumber(context, start, str + char, comments);
225 }
226 else if (char == 'x' &&
227 (str == '0' || str == '-0') &&
228 (context.mode & JsonParseMode.HexadecimalNumberAllowed) != 0) {
229 return _readHexaNumber(context, str == '-0', start, comments);
230 }
231 else {
232 // We read one too many characters, so rollback the last character.
233 context.position = context.previous;
234 break;
235 }
236 str += char;
237 }
238 // We're done reading this number.
239 if (str.endsWith('.') && (context.mode & JsonParseMode.HexadecimalNumberAllowed) == 0) {
240 throw new InvalidJsonCharacterException(context);
241 }
242 return {
243 kind: 'number',
244 start,
245 end: context.position,
246 text: context.original.substring(start.offset, context.position.offset),
247 value: Number.parseFloat(str),
248 comments,
249 };
250}
251/**
252 * Read a string from the context. Takes the comments of the string or read the blanks before the
253 * string.
254 * @private
255 */
256function _readString(context, comments = _readBlanks(context)) {
257 const start = context.position;
258 // Consume the first string delimiter.
259 const delim = _token(context);
260 if ((context.mode & JsonParseMode.SingleQuotesAllowed) == 0) {
261 if (delim == "'") {
262 throw new InvalidJsonCharacterException(context);
263 }
264 }
265 let str = '';
266 while (true) {
267 let char = _token(context);
268 if (char == delim) {
269 return {
270 kind: 'string',
271 start,
272 end: context.position,
273 text: context.original.substring(start.offset, context.position.offset),
274 value: str,
275 comments: comments,
276 };
277 }
278 else if (char == '\\') {
279 char = _token(context);
280 switch (char) {
281 case '\\':
282 case '/':
283 case '"':
284 case delim:
285 str += char;
286 break;
287 case 'b':
288 str += '\b';
289 break;
290 case 'f':
291 str += '\f';
292 break;
293 case 'n':
294 str += '\n';
295 break;
296 case 'r':
297 str += '\r';
298 break;
299 case 't':
300 str += '\t';
301 break;
302 case 'u':
303 const [c0] = _token(context, '0123456789abcdefABCDEF');
304 const [c1] = _token(context, '0123456789abcdefABCDEF');
305 const [c2] = _token(context, '0123456789abcdefABCDEF');
306 const [c3] = _token(context, '0123456789abcdefABCDEF');
307 str += String.fromCharCode(parseInt(c0 + c1 + c2 + c3, 16));
308 break;
309 case undefined:
310 throw new UnexpectedEndOfInputException(context);
311 case '\n':
312 // Only valid when multiline strings are allowed.
313 if ((context.mode & JsonParseMode.MultiLineStringAllowed) == 0) {
314 throw new InvalidJsonCharacterException(context);
315 }
316 str += char;
317 break;
318 default:
319 throw new InvalidJsonCharacterException(context);
320 }
321 }
322 else if (char === undefined) {
323 throw new UnexpectedEndOfInputException(context);
324 }
325 else if (char == '\b' || char == '\f' || char == '\n' || char == '\r' || char == '\t') {
326 throw new InvalidJsonCharacterException(context);
327 }
328 else {
329 str += char;
330 }
331 }
332}
333/**
334 * Read the constant `true` from the context.
335 * @private
336 */
337function _readTrue(context, comments = _readBlanks(context)) {
338 const start = context.position;
339 _token(context, 't');
340 _token(context, 'r');
341 _token(context, 'u');
342 _token(context, 'e');
343 const end = context.position;
344 return {
345 kind: 'true',
346 start,
347 end,
348 text: context.original.substring(start.offset, end.offset),
349 value: true,
350 comments,
351 };
352}
353/**
354 * Read the constant `false` from the context.
355 * @private
356 */
357function _readFalse(context, comments = _readBlanks(context)) {
358 const start = context.position;
359 _token(context, 'f');
360 _token(context, 'a');
361 _token(context, 'l');
362 _token(context, 's');
363 _token(context, 'e');
364 const end = context.position;
365 return {
366 kind: 'false',
367 start,
368 end,
369 text: context.original.substring(start.offset, end.offset),
370 value: false,
371 comments,
372 };
373}
374/**
375 * Read the constant `null` from the context.
376 * @private
377 */
378function _readNull(context, comments = _readBlanks(context)) {
379 const start = context.position;
380 _token(context, 'n');
381 _token(context, 'u');
382 _token(context, 'l');
383 _token(context, 'l');
384 const end = context.position;
385 return {
386 kind: 'null',
387 start,
388 end,
389 text: context.original.substring(start.offset, end.offset),
390 value: null,
391 comments: comments,
392 };
393}
394/**
395 * Read the constant `NaN` from the context.
396 * @private
397 */
398function _readNaN(context, comments = _readBlanks(context)) {
399 const start = context.position;
400 _token(context, 'N');
401 _token(context, 'a');
402 _token(context, 'N');
403 const end = context.position;
404 return {
405 kind: 'number',
406 start,
407 end,
408 text: context.original.substring(start.offset, end.offset),
409 value: NaN,
410 comments: comments,
411 };
412}
413/**
414 * Read an array of JSON values from the context.
415 * @private
416 */
417function _readArray(context, comments = _readBlanks(context)) {
418 const start = context.position;
419 // Consume the first delimiter.
420 _token(context, '[');
421 const value = [];
422 const elements = [];
423 _readBlanks(context);
424 if (_peek(context) != ']') {
425 const node = _readValue(context);
426 elements.push(node);
427 value.push(node.value);
428 }
429 while (_peek(context) != ']') {
430 _token(context, ',');
431 const valueComments = _readBlanks(context);
432 if ((context.mode & JsonParseMode.TrailingCommasAllowed) !== 0 && _peek(context) === ']') {
433 break;
434 }
435 const node = _readValue(context, valueComments);
436 elements.push(node);
437 value.push(node.value);
438 }
439 _token(context, ']');
440 return {
441 kind: 'array',
442 start,
443 end: context.position,
444 text: context.original.substring(start.offset, context.position.offset),
445 value,
446 elements,
447 comments,
448 };
449}
450/**
451 * Read an identifier from the context. An identifier is a valid JavaScript identifier, and this
452 * function is only used in Loose mode.
453 * @private
454 */
455function _readIdentifier(context, comments = _readBlanks(context)) {
456 const start = context.position;
457 let char = _peek(context);
458 if (char && '0123456789'.indexOf(char) != -1) {
459 const identifierNode = _readNumber(context);
460 return {
461 kind: 'identifier',
462 start,
463 end: identifierNode.end,
464 text: identifierNode.text,
465 value: identifierNode.value.toString(),
466 };
467 }
468 const identValidFirstChar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ';
469 const identValidChar = '_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ0123456789';
470 let first = true;
471 let value = '';
472 while (true) {
473 char = _token(context);
474 if (char == undefined ||
475 (first ? identValidFirstChar.indexOf(char) : identValidChar.indexOf(char)) == -1) {
476 context.position = context.previous;
477 return {
478 kind: 'identifier',
479 start,
480 end: context.position,
481 text: context.original.substr(start.offset, context.position.offset),
482 value,
483 comments,
484 };
485 }
486 value += char;
487 first = false;
488 }
489}
490/**
491 * Read a property from the context. A property is a string or (in Loose mode only) a number or
492 * an identifier, followed by a colon `:`.
493 * @private
494 */
495function _readProperty(context, comments = _readBlanks(context)) {
496 const start = context.position;
497 let key;
498 if ((context.mode & JsonParseMode.IdentifierKeyNamesAllowed) != 0) {
499 const top = _peek(context);
500 if (top == '"' || top == "'") {
501 key = _readString(context);
502 }
503 else {
504 key = _readIdentifier(context);
505 }
506 }
507 else {
508 key = _readString(context);
509 }
510 _readBlanks(context);
511 _token(context, ':');
512 const value = _readValue(context);
513 const end = context.position;
514 return {
515 kind: 'keyvalue',
516 key,
517 value,
518 start,
519 end,
520 text: context.original.substring(start.offset, end.offset),
521 comments,
522 };
523}
524/**
525 * Read an object of properties -> JSON values from the context.
526 * @private
527 */
528function _readObject(context, comments = _readBlanks(context)) {
529 const start = context.position;
530 // Consume the first delimiter.
531 _token(context, '{');
532 const value = {};
533 const properties = [];
534 _readBlanks(context);
535 if (_peek(context) != '}') {
536 const property = _readProperty(context);
537 value[property.key.value] = property.value.value;
538 properties.push(property);
539 while (_peek(context) != '}') {
540 _token(context, ',');
541 const propertyComments = _readBlanks(context);
542 if ((context.mode & JsonParseMode.TrailingCommasAllowed) !== 0 && _peek(context) === '}') {
543 break;
544 }
545 const property = _readProperty(context, propertyComments);
546 value[property.key.value] = property.value.value;
547 properties.push(property);
548 }
549 }
550 _token(context, '}');
551 return {
552 kind: 'object',
553 properties,
554 start,
555 end: context.position,
556 value,
557 text: context.original.substring(start.offset, context.position.offset),
558 comments,
559 };
560}
561/**
562 * Remove any blank character or comments (in Loose mode) from the context, returning an array
563 * of comments if any are found.
564 * @private
565 */
566function _readBlanks(context) {
567 if ((context.mode & JsonParseMode.CommentsAllowed) != 0) {
568 const comments = [];
569 while (true) {
570 const char = context.original[context.position.offset];
571 if (char == '/' && context.original[context.position.offset + 1] == '*') {
572 const start = context.position;
573 // Multi line comment.
574 _next(context);
575 _next(context);
576 while (context.original[context.position.offset] != '*' ||
577 context.original[context.position.offset + 1] != '/') {
578 _next(context);
579 if (context.position.offset >= context.original.length) {
580 throw new UnexpectedEndOfInputException(context);
581 }
582 }
583 // Remove "*/".
584 _next(context);
585 _next(context);
586 comments.push({
587 kind: 'multicomment',
588 start,
589 end: context.position,
590 text: context.original.substring(start.offset, context.position.offset),
591 content: context.original.substring(start.offset + 2, context.position.offset - 2),
592 });
593 }
594 else if (char == '/' && context.original[context.position.offset + 1] == '/') {
595 const start = context.position;
596 // Multi line comment.
597 _next(context);
598 _next(context);
599 while (context.original[context.position.offset] != '\n') {
600 _next(context);
601 if (context.position.offset >= context.original.length) {
602 break;
603 }
604 }
605 // Remove "\n".
606 if (context.position.offset < context.original.length) {
607 _next(context);
608 }
609 comments.push({
610 kind: 'comment',
611 start,
612 end: context.position,
613 text: context.original.substring(start.offset, context.position.offset),
614 content: context.original.substring(start.offset + 2, context.position.offset - 1),
615 });
616 }
617 else if (char == ' ' || char == '\t' || char == '\n' || char == '\r' || char == '\f') {
618 _next(context);
619 }
620 else {
621 break;
622 }
623 }
624 return comments;
625 }
626 else {
627 let char = context.original[context.position.offset];
628 while (char == ' ' || char == '\t' || char == '\n' || char == '\r' || char == '\f') {
629 _next(context);
630 char = context.original[context.position.offset];
631 }
632 return [];
633 }
634}
635/**
636 * Read a JSON value from the context, which can be any form of JSON value.
637 * @private
638 */
639function _readValue(context, comments = _readBlanks(context)) {
640 let result;
641 // Clean up before.
642 const char = _peek(context);
643 switch (char) {
644 case undefined:
645 throw new UnexpectedEndOfInputException(context);
646 case '-':
647 case '0':
648 case '1':
649 case '2':
650 case '3':
651 case '4':
652 case '5':
653 case '6':
654 case '7':
655 case '8':
656 case '9':
657 result = _readNumber(context, comments);
658 break;
659 case '.':
660 case '+':
661 if ((context.mode & JsonParseMode.LaxNumberParsingAllowed) == 0) {
662 throw new InvalidJsonCharacterException(context);
663 }
664 result = _readNumber(context, comments);
665 break;
666 case "'":
667 case '"':
668 result = _readString(context, comments);
669 break;
670 case 'I':
671 if ((context.mode & JsonParseMode.NumberConstantsAllowed) == 0) {
672 throw new InvalidJsonCharacterException(context);
673 }
674 result = _readNumber(context, comments);
675 break;
676 case 'N':
677 if ((context.mode & JsonParseMode.NumberConstantsAllowed) == 0) {
678 throw new InvalidJsonCharacterException(context);
679 }
680 result = _readNaN(context, comments);
681 break;
682 case 't':
683 result = _readTrue(context, comments);
684 break;
685 case 'f':
686 result = _readFalse(context, comments);
687 break;
688 case 'n':
689 result = _readNull(context, comments);
690 break;
691 case '[':
692 result = _readArray(context, comments);
693 break;
694 case '{':
695 result = _readObject(context, comments);
696 break;
697 default:
698 throw new InvalidJsonCharacterException(context);
699 }
700 // Clean up after.
701 _readBlanks(context);
702 return result;
703}
704/**
705 * The Parse mode used for parsing the JSON string.
706 */
707var JsonParseMode;
708(function (JsonParseMode) {
709 JsonParseMode[JsonParseMode["Strict"] = 0] = "Strict";
710 JsonParseMode[JsonParseMode["CommentsAllowed"] = 1] = "CommentsAllowed";
711 JsonParseMode[JsonParseMode["SingleQuotesAllowed"] = 2] = "SingleQuotesAllowed";
712 JsonParseMode[JsonParseMode["IdentifierKeyNamesAllowed"] = 4] = "IdentifierKeyNamesAllowed";
713 JsonParseMode[JsonParseMode["TrailingCommasAllowed"] = 8] = "TrailingCommasAllowed";
714 JsonParseMode[JsonParseMode["HexadecimalNumberAllowed"] = 16] = "HexadecimalNumberAllowed";
715 JsonParseMode[JsonParseMode["MultiLineStringAllowed"] = 32] = "MultiLineStringAllowed";
716 JsonParseMode[JsonParseMode["LaxNumberParsingAllowed"] = 64] = "LaxNumberParsingAllowed";
717 JsonParseMode[JsonParseMode["NumberConstantsAllowed"] = 128] = "NumberConstantsAllowed";
718 JsonParseMode[JsonParseMode["Default"] = 0] = "Default";
719 JsonParseMode[JsonParseMode["Loose"] = 255] = "Loose";
720 JsonParseMode[JsonParseMode["Json"] = 0] = "Json";
721 JsonParseMode[JsonParseMode["Json5"] = 255] = "Json5";
722})(JsonParseMode = exports.JsonParseMode || (exports.JsonParseMode = {}));
723/**
724 * Parse the JSON string and return its AST. The AST may be losing data (end comments are
725 * discarded for example, and space characters are not represented in the AST), but all values
726 * will have a single node in the AST (a 1-to-1 mapping).
727 *
728 * @deprecated Deprecated since version 11. Use 3rd party JSON parsers such as `jsonc-parser` instead.
729 * @param input The string to use.
730 * @param mode The mode to parse the input with. {@see JsonParseMode}.
731 * @returns {JsonAstNode} The root node of the value of the AST.
732 */
733function parseJsonAst(input, mode = JsonParseMode.Default) {
734 if (mode == JsonParseMode.Default) {
735 mode = JsonParseMode.Strict;
736 }
737 const context = {
738 position: { offset: 0, line: 0, character: 0 },
739 previous: { offset: 0, line: 0, character: 0 },
740 original: input,
741 comments: undefined,
742 mode,
743 };
744 const ast = _readValue(context);
745 if (context.position.offset < input.length) {
746 const rest = input.substr(context.position.offset);
747 const i = rest.length > 20 ? rest.substr(0, 20) + '...' : rest;
748 throw new Error(`Expected end of file, got "${i}" at ` +
749 `${context.position.line}:${context.position.character}.`);
750 }
751 return ast;
752}
753exports.parseJsonAst = parseJsonAst;
754/**
755 * Parse a JSON string into its value. This discards the AST and only returns the value itself.
756 *
757 * If a path option is pass, it also absorbs JSON parsing errors and return a new error with the
758 * path in it. Useful for showing errors when parsing from a file.
759 *
760 * @deprecated Deprecated since version 11. Use 3rd party JSON parsers such as `jsonc-parser` instead.
761 * @param input The string to parse.
762 * @param mode The mode to parse the input with. {@see JsonParseMode}.
763 * @param options Additional optinos for parsing.
764 * @returns {JsonValue} The value represented by the JSON string.
765 */
766function parseJson(input, mode = JsonParseMode.Default, options) {
767 try {
768 // Try parsing for the fastest path available, if error, uses our own parser for better errors.
769 if (mode == JsonParseMode.Strict) {
770 try {
771 return JSON.parse(input);
772 }
773 catch (err) {
774 return parseJsonAst(input, mode).value;
775 }
776 }
777 return parseJsonAst(input, mode).value;
778 }
779 catch (e) {
780 if (options && options.path && e instanceof JsonException) {
781 throw new PathSpecificJsonException(options.path, e);
782 }
783 throw e;
784 }
785}
786exports.parseJson = parseJson;
Note: See TracBrowser for help on using the repository browser.