1 | 'use strict';
|
---|
2 | var $ = require('../internals/export');
|
---|
3 | var DESCRIPTORS = require('../internals/descriptors');
|
---|
4 | var globalThis = require('../internals/global-this');
|
---|
5 | var getBuiltIn = require('../internals/get-built-in');
|
---|
6 | var uncurryThis = require('../internals/function-uncurry-this');
|
---|
7 | var call = require('../internals/function-call');
|
---|
8 | var isCallable = require('../internals/is-callable');
|
---|
9 | var isObject = require('../internals/is-object');
|
---|
10 | var isArray = require('../internals/is-array');
|
---|
11 | var hasOwn = require('../internals/has-own-property');
|
---|
12 | var toString = require('../internals/to-string');
|
---|
13 | var lengthOfArrayLike = require('../internals/length-of-array-like');
|
---|
14 | var createProperty = require('../internals/create-property');
|
---|
15 | var fails = require('../internals/fails');
|
---|
16 | var parseJSONString = require('../internals/parse-json-string');
|
---|
17 | var NATIVE_SYMBOL = require('../internals/symbol-constructor-detection');
|
---|
18 |
|
---|
19 | var JSON = globalThis.JSON;
|
---|
20 | var Number = globalThis.Number;
|
---|
21 | var SyntaxError = globalThis.SyntaxError;
|
---|
22 | var nativeParse = JSON && JSON.parse;
|
---|
23 | var enumerableOwnProperties = getBuiltIn('Object', 'keys');
|
---|
24 | // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
|
---|
25 | var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
---|
26 | var at = uncurryThis(''.charAt);
|
---|
27 | var slice = uncurryThis(''.slice);
|
---|
28 | var exec = uncurryThis(/./.exec);
|
---|
29 | var push = uncurryThis([].push);
|
---|
30 |
|
---|
31 | var IS_DIGIT = /^\d$/;
|
---|
32 | var IS_NON_ZERO_DIGIT = /^[1-9]$/;
|
---|
33 | var IS_NUMBER_START = /^[\d-]$/;
|
---|
34 | var IS_WHITESPACE = /^[\t\n\r ]$/;
|
---|
35 |
|
---|
36 | var PRIMITIVE = 0;
|
---|
37 | var OBJECT = 1;
|
---|
38 |
|
---|
39 | var $parse = function (source, reviver) {
|
---|
40 | source = toString(source);
|
---|
41 | var context = new Context(source, 0, '');
|
---|
42 | var root = context.parse();
|
---|
43 | var value = root.value;
|
---|
44 | var endIndex = context.skip(IS_WHITESPACE, root.end);
|
---|
45 | if (endIndex < source.length) {
|
---|
46 | throw new SyntaxError('Unexpected extra character: "' + at(source, endIndex) + '" after the parsed data at: ' + endIndex);
|
---|
47 | }
|
---|
48 | return isCallable(reviver) ? internalize({ '': value }, '', reviver, root) : value;
|
---|
49 | };
|
---|
50 |
|
---|
51 | var internalize = function (holder, name, reviver, node) {
|
---|
52 | var val = holder[name];
|
---|
53 | var unmodified = node && val === node.value;
|
---|
54 | var context = unmodified && typeof node.source == 'string' ? { source: node.source } : {};
|
---|
55 | var elementRecordsLen, keys, len, i, P;
|
---|
56 | if (isObject(val)) {
|
---|
57 | var nodeIsArray = isArray(val);
|
---|
58 | var nodes = unmodified ? node.nodes : nodeIsArray ? [] : {};
|
---|
59 | if (nodeIsArray) {
|
---|
60 | elementRecordsLen = nodes.length;
|
---|
61 | len = lengthOfArrayLike(val);
|
---|
62 | for (i = 0; i < len; i++) {
|
---|
63 | internalizeProperty(val, i, internalize(val, '' + i, reviver, i < elementRecordsLen ? nodes[i] : undefined));
|
---|
64 | }
|
---|
65 | } else {
|
---|
66 | keys = enumerableOwnProperties(val);
|
---|
67 | len = lengthOfArrayLike(keys);
|
---|
68 | for (i = 0; i < len; i++) {
|
---|
69 | P = keys[i];
|
---|
70 | internalizeProperty(val, P, internalize(val, P, reviver, hasOwn(nodes, P) ? nodes[P] : undefined));
|
---|
71 | }
|
---|
72 | }
|
---|
73 | }
|
---|
74 | return call(reviver, holder, name, val, context);
|
---|
75 | };
|
---|
76 |
|
---|
77 | var internalizeProperty = function (object, key, value) {
|
---|
78 | if (DESCRIPTORS) {
|
---|
79 | var descriptor = getOwnPropertyDescriptor(object, key);
|
---|
80 | if (descriptor && !descriptor.configurable) return;
|
---|
81 | }
|
---|
82 | if (value === undefined) delete object[key];
|
---|
83 | else createProperty(object, key, value);
|
---|
84 | };
|
---|
85 |
|
---|
86 | var Node = function (value, end, source, nodes) {
|
---|
87 | this.value = value;
|
---|
88 | this.end = end;
|
---|
89 | this.source = source;
|
---|
90 | this.nodes = nodes;
|
---|
91 | };
|
---|
92 |
|
---|
93 | var Context = function (source, index) {
|
---|
94 | this.source = source;
|
---|
95 | this.index = index;
|
---|
96 | };
|
---|
97 |
|
---|
98 | // https://www.json.org/json-en.html
|
---|
99 | Context.prototype = {
|
---|
100 | fork: function (nextIndex) {
|
---|
101 | return new Context(this.source, nextIndex);
|
---|
102 | },
|
---|
103 | parse: function () {
|
---|
104 | var source = this.source;
|
---|
105 | var i = this.skip(IS_WHITESPACE, this.index);
|
---|
106 | var fork = this.fork(i);
|
---|
107 | var chr = at(source, i);
|
---|
108 | if (exec(IS_NUMBER_START, chr)) return fork.number();
|
---|
109 | switch (chr) {
|
---|
110 | case '{':
|
---|
111 | return fork.object();
|
---|
112 | case '[':
|
---|
113 | return fork.array();
|
---|
114 | case '"':
|
---|
115 | return fork.string();
|
---|
116 | case 't':
|
---|
117 | return fork.keyword(true);
|
---|
118 | case 'f':
|
---|
119 | return fork.keyword(false);
|
---|
120 | case 'n':
|
---|
121 | return fork.keyword(null);
|
---|
122 | } throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i);
|
---|
123 | },
|
---|
124 | node: function (type, value, start, end, nodes) {
|
---|
125 | return new Node(value, end, type ? null : slice(this.source, start, end), nodes);
|
---|
126 | },
|
---|
127 | object: function () {
|
---|
128 | var source = this.source;
|
---|
129 | var i = this.index + 1;
|
---|
130 | var expectKeypair = false;
|
---|
131 | var object = {};
|
---|
132 | var nodes = {};
|
---|
133 | while (i < source.length) {
|
---|
134 | i = this.until(['"', '}'], i);
|
---|
135 | if (at(source, i) === '}' && !expectKeypair) {
|
---|
136 | i++;
|
---|
137 | break;
|
---|
138 | }
|
---|
139 | // Parsing the key
|
---|
140 | var result = this.fork(i).string();
|
---|
141 | var key = result.value;
|
---|
142 | i = result.end;
|
---|
143 | i = this.until([':'], i) + 1;
|
---|
144 | // Parsing value
|
---|
145 | i = this.skip(IS_WHITESPACE, i);
|
---|
146 | result = this.fork(i).parse();
|
---|
147 | createProperty(nodes, key, result);
|
---|
148 | createProperty(object, key, result.value);
|
---|
149 | i = this.until([',', '}'], result.end);
|
---|
150 | var chr = at(source, i);
|
---|
151 | if (chr === ',') {
|
---|
152 | expectKeypair = true;
|
---|
153 | i++;
|
---|
154 | } else if (chr === '}') {
|
---|
155 | i++;
|
---|
156 | break;
|
---|
157 | }
|
---|
158 | }
|
---|
159 | return this.node(OBJECT, object, this.index, i, nodes);
|
---|
160 | },
|
---|
161 | array: function () {
|
---|
162 | var source = this.source;
|
---|
163 | var i = this.index + 1;
|
---|
164 | var expectElement = false;
|
---|
165 | var array = [];
|
---|
166 | var nodes = [];
|
---|
167 | while (i < source.length) {
|
---|
168 | i = this.skip(IS_WHITESPACE, i);
|
---|
169 | if (at(source, i) === ']' && !expectElement) {
|
---|
170 | i++;
|
---|
171 | break;
|
---|
172 | }
|
---|
173 | var result = this.fork(i).parse();
|
---|
174 | push(nodes, result);
|
---|
175 | push(array, result.value);
|
---|
176 | i = this.until([',', ']'], result.end);
|
---|
177 | if (at(source, i) === ',') {
|
---|
178 | expectElement = true;
|
---|
179 | i++;
|
---|
180 | } else if (at(source, i) === ']') {
|
---|
181 | i++;
|
---|
182 | break;
|
---|
183 | }
|
---|
184 | }
|
---|
185 | return this.node(OBJECT, array, this.index, i, nodes);
|
---|
186 | },
|
---|
187 | string: function () {
|
---|
188 | var index = this.index;
|
---|
189 | var parsed = parseJSONString(this.source, this.index + 1);
|
---|
190 | return this.node(PRIMITIVE, parsed.value, index, parsed.end);
|
---|
191 | },
|
---|
192 | number: function () {
|
---|
193 | var source = this.source;
|
---|
194 | var startIndex = this.index;
|
---|
195 | var i = startIndex;
|
---|
196 | if (at(source, i) === '-') i++;
|
---|
197 | if (at(source, i) === '0') i++;
|
---|
198 | else if (exec(IS_NON_ZERO_DIGIT, at(source, i))) i = this.skip(IS_DIGIT, i + 1);
|
---|
199 | else throw new SyntaxError('Failed to parse number at: ' + i);
|
---|
200 | if (at(source, i) === '.') i = this.skip(IS_DIGIT, i + 1);
|
---|
201 | if (at(source, i) === 'e' || at(source, i) === 'E') {
|
---|
202 | i++;
|
---|
203 | if (at(source, i) === '+' || at(source, i) === '-') i++;
|
---|
204 | var exponentStartIndex = i;
|
---|
205 | i = this.skip(IS_DIGIT, i);
|
---|
206 | if (exponentStartIndex === i) throw new SyntaxError("Failed to parse number's exponent value at: " + i);
|
---|
207 | }
|
---|
208 | return this.node(PRIMITIVE, Number(slice(source, startIndex, i)), startIndex, i);
|
---|
209 | },
|
---|
210 | keyword: function (value) {
|
---|
211 | var keyword = '' + value;
|
---|
212 | var index = this.index;
|
---|
213 | var endIndex = index + keyword.length;
|
---|
214 | if (slice(this.source, index, endIndex) !== keyword) throw new SyntaxError('Failed to parse value at: ' + index);
|
---|
215 | return this.node(PRIMITIVE, value, index, endIndex);
|
---|
216 | },
|
---|
217 | skip: function (regex, i) {
|
---|
218 | var source = this.source;
|
---|
219 | for (; i < source.length; i++) if (!exec(regex, at(source, i))) break;
|
---|
220 | return i;
|
---|
221 | },
|
---|
222 | until: function (array, i) {
|
---|
223 | i = this.skip(IS_WHITESPACE, i);
|
---|
224 | var chr = at(this.source, i);
|
---|
225 | for (var j = 0; j < array.length; j++) if (array[j] === chr) return i;
|
---|
226 | throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i);
|
---|
227 | }
|
---|
228 | };
|
---|
229 |
|
---|
230 | var NO_SOURCE_SUPPORT = fails(function () {
|
---|
231 | var unsafeInt = '9007199254740993';
|
---|
232 | var source;
|
---|
233 | nativeParse(unsafeInt, function (key, value, context) {
|
---|
234 | source = context.source;
|
---|
235 | });
|
---|
236 | return source !== unsafeInt;
|
---|
237 | });
|
---|
238 |
|
---|
239 | var PROPER_BASE_PARSE = NATIVE_SYMBOL && !fails(function () {
|
---|
240 | // Safari 9 bug
|
---|
241 | return 1 / nativeParse('-0 \t') !== -Infinity;
|
---|
242 | });
|
---|
243 |
|
---|
244 | // `JSON.parse` method
|
---|
245 | // https://tc39.es/ecma262/#sec-json.parse
|
---|
246 | // https://github.com/tc39/proposal-json-parse-with-source
|
---|
247 | $({ target: 'JSON', stat: true, forced: NO_SOURCE_SUPPORT }, {
|
---|
248 | parse: function parse(text, reviver) {
|
---|
249 | return PROPER_BASE_PARSE && !isCallable(reviver) ? nativeParse(text) : $parse(text, reviver);
|
---|
250 | }
|
---|
251 | });
|
---|