@prantlf/jsonlint 14.0.2 → 14.1.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/LICENSE +1 -1
- package/README.md +13 -2
- package/lib/cli.js +62 -13
- package/lib/formatter.js +3 -3
- package/lib/jsonlint.js +76 -73
- package/lib/printer.js +16 -8
- package/lib/sorter.js +22 -5
- package/lib/validator.js +1 -1
- package/package.json +25 -44
- package/web/ajv.min.js +5 -5
- package/web/ajv.min.js.map +1 -1
- package/web/formatter.min.js +4 -4
- package/web/formatter.min.js.map +2 -2
- package/web/jsonlint.html +27 -2
- package/web/jsonlint.min.js +9 -9
- package/web/jsonlint.min.js.map +3 -3
- package/web/printer.min.js +3 -3
- package/web/printer.min.js.map +2 -2
- package/web/sorter.min.js +1 -1
- package/web/sorter.min.js.map +3 -3
- package/web/validator.min.js +1 -1
- package/web/validator.min.js.map +2 -2
- package/CHANGELOG.md +0 -440
package/LICENSE
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2012-2018 Zachary Carter
|
|
4
|
-
Copyright (c) 2019-
|
|
4
|
+
Copyright (c) 2019-2024 Ferdinand Prantl
|
|
5
5
|
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This is a fork of the original project ([zaach/jsonlint](https://github.com/zaac
|
|
|
21
21
|
* Offers pretty-printing including comment-stripping and object keys without quotes (JSON5).
|
|
22
22
|
* Prefers the native JSON parser if possible to run [10x faster than the custom parser].
|
|
23
23
|
* Reports errors with rich additional information. From the JSON Schema validation too.
|
|
24
|
-
* Consumes configuration from both command line and [configuration files](configuration).
|
|
24
|
+
* Consumes configuration from both command line and [configuration files](#configuration).
|
|
25
25
|
* Implements JavaScript modules using [UMD] to work in Node.js, in a browser, everywhere.
|
|
26
26
|
* Depends on up-to-date npm modules with no installation warnings.
|
|
27
27
|
* Small size - 18.4 kB minified, 6.45 kB gzipped, 5.05 kB brotlied.
|
|
@@ -121,6 +121,12 @@ Usage: `jsonlint [options] [--] [<file, directory, pattern> ...]`
|
|
|
121
121
|
-f, --config <file> read options from a custom configuration file
|
|
122
122
|
-F, --no-config disable searching for configuration files
|
|
123
123
|
-s, --sort-keys sort object keys (not when prettifying)
|
|
124
|
+
--sort-keys-ignore-case sort object keys ignoring the letter case
|
|
125
|
+
--sort-keys-locale <id> locale identifier to sort object keys with
|
|
126
|
+
(or "default" for the system default)
|
|
127
|
+
--sort-keys-case-first <id> order if only letter case is different
|
|
128
|
+
("upper", "lower" and "false" are allowed)
|
|
129
|
+
--sort-keys-numeric sort by numbers recognised in object keys
|
|
124
130
|
-E, --extensions <ext...> file extensions to process for directory walk
|
|
125
131
|
(default: json, JSON)
|
|
126
132
|
-i, --in-place overwrite the input files
|
|
@@ -154,6 +160,7 @@ Usage: `jsonlint [options] [--] [<file, directory, pattern> ...]`
|
|
|
154
160
|
--enforce-double-quotes surrounds all strings with double quotes
|
|
155
161
|
--enforce-single-quotes surrounds all strings with single quotes
|
|
156
162
|
--trim-trailing-commas omit trailing commas from objects and arrays
|
|
163
|
+
--succeed-with-no-files succeed (exit code 0) if no files were found
|
|
157
164
|
-v, --version output the version number
|
|
158
165
|
-h, --help display help for command
|
|
159
166
|
|
|
@@ -193,6 +200,10 @@ The configuration is an object with the following properties, described above, w
|
|
|
193
200
|
| --------- | ----- |
|
|
194
201
|
| patterns | |
|
|
195
202
|
| sort-keys | sortKeys |
|
|
203
|
+
| sort-keys-ignore-case | sortKeysIgnoreCase |
|
|
204
|
+
| sort-keys-locale | sortKeysLocale |
|
|
205
|
+
| sort-keys-case-first | sortKeysCaseFirst |
|
|
206
|
+
| sort-keys-numeric | sortKeysNumeric |
|
|
196
207
|
| extensions | |
|
|
197
208
|
| in-place | inPlace |
|
|
198
209
|
| diff | |
|
|
@@ -428,7 +439,7 @@ ${reason}`)
|
|
|
428
439
|
|
|
429
440
|
## License
|
|
430
441
|
|
|
431
|
-
Copyright (C) 2012-
|
|
442
|
+
Copyright (C) 2012-2024 Zachary Carter, Ferdinand Prantl
|
|
432
443
|
|
|
433
444
|
Licensed under the [MIT License].
|
|
434
445
|
|
package/lib/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { readdirSync, readFileSync, statSync, writeFileSync } = require('fs')
|
|
4
|
-
const { extname, join } = require('path')
|
|
3
|
+
const { readdirSync, readFileSync, statSync, writeFileSync } = require('node:fs')
|
|
4
|
+
const { extname, join } = require('node:path')
|
|
5
5
|
const { isDynamicPattern, sync } = require('fast-glob')
|
|
6
6
|
const { parse, tokenize } = require('./jsonlint')
|
|
7
7
|
const { format } = require('./formatter')
|
|
@@ -18,6 +18,12 @@ Options:
|
|
|
18
18
|
-f, --config <file> read options from a custom configuration file
|
|
19
19
|
-F, --no-config disable searching for configuration files
|
|
20
20
|
-s, --sort-keys sort object keys (not when prettifying)
|
|
21
|
+
--sort-keys-ignore-case sort object keys ignoring the letter case
|
|
22
|
+
--sort-keys-locale <id> locale identifier to sort object keys with
|
|
23
|
+
(or "default" for the system default)
|
|
24
|
+
--sort-keys-case-first <id> order if only letter case is different
|
|
25
|
+
("upper", "lower" and "false" are allowed)
|
|
26
|
+
--sort-keys-numeric sort by numbers recognised in object keys
|
|
21
27
|
-E, --extensions <ext...> file extensions to process for directory walk
|
|
22
28
|
(default: json, JSON)
|
|
23
29
|
-i, --in-place overwrite the input files
|
|
@@ -51,6 +57,7 @@ Options:
|
|
|
51
57
|
--enforce-double-quotes surrounds all strings with double quotes
|
|
52
58
|
--enforce-single-quotes surrounds all strings with single quotes
|
|
53
59
|
--trim-trailing-commas omit trailing commas from objects and arrays
|
|
60
|
+
--succeed-with-no-files succeed (exit code 0) if no files were found
|
|
54
61
|
-v, --version output the version number
|
|
55
62
|
-h, --help display help for command
|
|
56
63
|
|
|
@@ -85,6 +92,18 @@ for (let i = 2, l = argv.length; i < l; ++i) {
|
|
|
85
92
|
case 's': case 'sort-keys':
|
|
86
93
|
params.sortKeys = flag
|
|
87
94
|
return
|
|
95
|
+
case 'sort-keys-ignore-case':
|
|
96
|
+
params.sortKeysIgnoreCase = flag
|
|
97
|
+
return
|
|
98
|
+
case 'sort-keys-locale':
|
|
99
|
+
params.sortKeysLocale = match[4] || argv[++i]
|
|
100
|
+
return
|
|
101
|
+
case 'sort-keys-case-first':
|
|
102
|
+
params.sortKeysCaseFirst = match[4] || argv[++i]
|
|
103
|
+
return
|
|
104
|
+
case 'sort-keys-numeric':
|
|
105
|
+
params.sortKeysNumeric = flag
|
|
106
|
+
return
|
|
88
107
|
case 'E': case 'extensions':
|
|
89
108
|
arg = match[4] || argv[++i]
|
|
90
109
|
params.extensions.push(...arg.split(','))
|
|
@@ -100,7 +119,7 @@ for (let i = 2, l = argv.length; i < l; ++i) {
|
|
|
100
119
|
return
|
|
101
120
|
case 't': case 'indent':
|
|
102
121
|
arg = match[4] || argv[++i]
|
|
103
|
-
if (arg.trim().length > 0 && !isNaN(+arg)) arg = +arg
|
|
122
|
+
if (arg.trim().length > 0 && !Number.isNaN(+arg)) arg = +arg
|
|
104
123
|
params.indent = arg
|
|
105
124
|
return
|
|
106
125
|
case 'c': case 'compact':
|
|
@@ -149,7 +168,7 @@ for (let i = 2, l = argv.length; i < l; ++i) {
|
|
|
149
168
|
return
|
|
150
169
|
case 'x': case 'context':
|
|
151
170
|
arg = match[4] || argv[++i]
|
|
152
|
-
if (isNaN(+arg)) {
|
|
171
|
+
if (Number.isNaN(+arg)) {
|
|
153
172
|
throw new Error(`invalid diff context: "${arg}"`)
|
|
154
173
|
}
|
|
155
174
|
params.indent = +arg
|
|
@@ -190,6 +209,9 @@ for (let i = 2, l = argv.length; i < l; ++i) {
|
|
|
190
209
|
case 'trim-trailing-commas':
|
|
191
210
|
params.trimTrailingCommas = flag
|
|
192
211
|
return
|
|
212
|
+
case 'succeed-with-no-files':
|
|
213
|
+
params.succeedWithNoFiles = flag
|
|
214
|
+
return
|
|
193
215
|
case 'v': case 'version':
|
|
194
216
|
console.log(require('../package.json').version)
|
|
195
217
|
process.exit(0)
|
|
@@ -226,6 +248,10 @@ const paramNames = {
|
|
|
226
248
|
'enforce-single-quotes': 'enforceSingleQuotes',
|
|
227
249
|
'trim-trailing-commas': 'trimTrailingCommas',
|
|
228
250
|
'sort-keys': 'sortKeys',
|
|
251
|
+
'sort-keys-ignore-case': 'sortKeysIgnoreCase',
|
|
252
|
+
'sort-keys-locale': 'sortKeysLocale',
|
|
253
|
+
'sort-keys-case-first': 'sortKeysCaseFirst',
|
|
254
|
+
'sort-keys-numeric': 'sortKeysNumeric',
|
|
229
255
|
'pretty-print-invalid': 'prettyPrintInvalid',
|
|
230
256
|
'log-files': 'logFiles',
|
|
231
257
|
'in-place': 'inPlace',
|
|
@@ -283,18 +309,19 @@ function logNormalError (error, file) {
|
|
|
283
309
|
}
|
|
284
310
|
|
|
285
311
|
function logCompactError (error, file) {
|
|
286
|
-
console.error(file
|
|
287
|
-
', col ' + error.location.start.column + ', ' + error.reason + '.')
|
|
312
|
+
console.error(`${file}: line ${error.location.start.line}, col ${error.location.start.column}, ${error.reason}.`)
|
|
288
313
|
}
|
|
289
314
|
|
|
290
315
|
function processContents (source, file) {
|
|
291
|
-
let parserOptions
|
|
316
|
+
let parserOptions
|
|
317
|
+
let parsed
|
|
318
|
+
let formatted
|
|
292
319
|
try {
|
|
293
320
|
parserOptions = {
|
|
294
321
|
mode: params.mode,
|
|
295
322
|
ignoreBOM: params.bom,
|
|
296
323
|
ignoreComments: params.comments,
|
|
297
|
-
ignoreTrailingCommas: params.trailingCommas,
|
|
324
|
+
ignoreTrailingCommas: params.trailingCommas || params.trimTrailingCommas,
|
|
298
325
|
allowSingleQuotedStrings: params.singleQuotedStrings,
|
|
299
326
|
allowDuplicateObjectKeys: params.duplicateKeys
|
|
300
327
|
}
|
|
@@ -329,8 +356,29 @@ function processContents (source, file) {
|
|
|
329
356
|
trimTrailingCommas: params.trimTrailingCommas
|
|
330
357
|
})
|
|
331
358
|
}
|
|
359
|
+
const sortOptions = {}
|
|
360
|
+
let sort
|
|
332
361
|
if (params.sortKeys) {
|
|
333
|
-
|
|
362
|
+
sort = true
|
|
363
|
+
}
|
|
364
|
+
if (params.sortKeysIgnoreCase) {
|
|
365
|
+
sortOptions.ignoreCase = true
|
|
366
|
+
sort = true
|
|
367
|
+
}
|
|
368
|
+
if (params.sortKeysLocale) {
|
|
369
|
+
sortOptions.locale = params.sortKeysLocale
|
|
370
|
+
sort = true
|
|
371
|
+
}
|
|
372
|
+
if (params.sortKeysCaseFirst) {
|
|
373
|
+
sortOptions.caseFirst = params.sortKeysCaseFirst
|
|
374
|
+
sort = true
|
|
375
|
+
}
|
|
376
|
+
if (params.sortKeysNumeric) {
|
|
377
|
+
sortOptions.numeric = true
|
|
378
|
+
sort = true
|
|
379
|
+
}
|
|
380
|
+
if (sort) {
|
|
381
|
+
parsed = sortObject(parsed, sortOptions)
|
|
334
382
|
}
|
|
335
383
|
return JSON.stringify(parsed, null, params.indent)
|
|
336
384
|
} catch (e) {
|
|
@@ -381,7 +429,7 @@ function ensureLineBreak (parsed, source) {
|
|
|
381
429
|
function checkContents (file, source, parsed) {
|
|
382
430
|
const { createTwoFilesPatch, structuredPatch } = require('diff')
|
|
383
431
|
const structured = structuredPatch(`${file}.orig`, file, source, parsed, '', '', { context: params.context })
|
|
384
|
-
const length = structured.hunks
|
|
432
|
+
const length = structured.hunks?.length
|
|
385
433
|
if (length > 0) {
|
|
386
434
|
const hunk = length === 1 ? 'hunk differs' : 'hunks differ'
|
|
387
435
|
const message = `${length} ${hunk}`
|
|
@@ -413,10 +461,11 @@ function checkContents (file, source, parsed) {
|
|
|
413
461
|
function diffContents (file, source, parsed) {
|
|
414
462
|
const { createTwoFilesPatch, structuredPatch } = require('diff')
|
|
415
463
|
const compact = params.quiet || params.compact
|
|
416
|
-
let diff
|
|
464
|
+
let diff
|
|
465
|
+
let length
|
|
417
466
|
if (compact) {
|
|
418
467
|
diff = structuredPatch(`${file}.orig`, file, source, parsed, '', '', { context: params.context })
|
|
419
|
-
length = diff.hunks
|
|
468
|
+
length = diff.hunks?.length
|
|
420
469
|
} else {
|
|
421
470
|
diff = createTwoFilesPatch(`${file}.orig`, file, source, parsed, '', '', { context: params.context })
|
|
422
471
|
length = diff.split(/\r?\n/).length - 4
|
|
@@ -487,7 +536,7 @@ function processPatterns (patterns) {
|
|
|
487
536
|
const files = sync(patterns, { onlyFiles: true })
|
|
488
537
|
if (!files.length) {
|
|
489
538
|
console.error('no files found')
|
|
490
|
-
process.exit(1)
|
|
539
|
+
process.exit(params.succeedWithNoFiles ? 0 : 1)
|
|
491
540
|
}
|
|
492
541
|
for (const file of files) {
|
|
493
542
|
try {
|
package/lib/formatter.js
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
case '{':
|
|
37
37
|
case '[':
|
|
38
38
|
if (!inString) {
|
|
39
|
-
outputString += currentChar
|
|
39
|
+
outputString += `${currentChar}\n${repeat(indentString, indentLevel + 1)}`
|
|
40
40
|
indentLevel += 1
|
|
41
41
|
} else {
|
|
42
42
|
outputString += currentChar
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
case ']':
|
|
47
47
|
if (!inString) {
|
|
48
48
|
indentLevel -= 1
|
|
49
|
-
outputString +=
|
|
49
|
+
outputString += `\n${repeat(indentString, indentLevel)}${currentChar}`
|
|
50
50
|
} else {
|
|
51
51
|
outputString += currentChar
|
|
52
52
|
}
|
|
53
53
|
break
|
|
54
54
|
case ',':
|
|
55
55
|
if (!inString) {
|
|
56
|
-
outputString +=
|
|
56
|
+
outputString += `,\n${repeat(indentString, indentLevel)}`
|
|
57
57
|
} else {
|
|
58
58
|
outputString += currentChar
|
|
59
59
|
}
|
package/lib/jsonlint.js
CHANGED
|
@@ -103,6 +103,8 @@ const unescapeMap = {
|
|
|
103
103
|
'/': '/'
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
const ownsProperty = Object.prototype.hasOwnProperty
|
|
107
|
+
|
|
106
108
|
function parseInternal (input, options) {
|
|
107
109
|
if (typeof input !== 'string' || !(input instanceof String)) {
|
|
108
110
|
input = String(input)
|
|
@@ -176,7 +178,7 @@ function parseInternal (input, options) {
|
|
|
176
178
|
let message
|
|
177
179
|
if (position < inputLength) {
|
|
178
180
|
const token = JSON.stringify(input[position])
|
|
179
|
-
message =
|
|
181
|
+
message = `Unexpected token ${token}`
|
|
180
182
|
} else {
|
|
181
183
|
message = 'Unexpected end of input'
|
|
182
184
|
}
|
|
@@ -220,87 +222,84 @@ function parseInternal (input, options) {
|
|
|
220
222
|
|
|
221
223
|
function parseGeneric () {
|
|
222
224
|
if (position < inputLength) {
|
|
223
|
-
startToken
|
|
225
|
+
startToken?.()
|
|
224
226
|
const char = input[position++]
|
|
225
227
|
if (char === '"' || (char === '\'' && allowSingleQuotedStrings)) {
|
|
226
228
|
const string = parseString(char)
|
|
227
|
-
endToken
|
|
229
|
+
endToken?.('literal', string)
|
|
228
230
|
return string
|
|
229
|
-
}
|
|
230
|
-
endToken
|
|
231
|
+
}if (char === '{') {
|
|
232
|
+
endToken?.('symbol', '{')
|
|
231
233
|
return parseObject()
|
|
232
|
-
}
|
|
233
|
-
endToken
|
|
234
|
+
}if (char === '[') {
|
|
235
|
+
endToken?.('symbol', '[')
|
|
234
236
|
return parseArray()
|
|
235
|
-
}
|
|
237
|
+
}if (char === '-' || char === '.' || isDecDigit(char) ||
|
|
236
238
|
(json5 && (char === '+' || char === 'I' || char === 'N'))) {
|
|
237
239
|
const number = parseNumber()
|
|
238
|
-
endToken
|
|
240
|
+
endToken?.('literal', number)
|
|
239
241
|
return number
|
|
240
|
-
}
|
|
242
|
+
}if (char === 'n') {
|
|
241
243
|
parseKeyword('null')
|
|
242
|
-
endToken
|
|
244
|
+
endToken?.('literal', null)
|
|
243
245
|
return null
|
|
244
|
-
}
|
|
246
|
+
}if (char === 't') {
|
|
245
247
|
parseKeyword('true')
|
|
246
|
-
endToken
|
|
248
|
+
endToken?.('literal', true)
|
|
247
249
|
return true
|
|
248
|
-
}
|
|
250
|
+
}if (char === 'f') {
|
|
249
251
|
parseKeyword('false')
|
|
250
|
-
endToken
|
|
252
|
+
endToken?.('literal', false)
|
|
251
253
|
return false
|
|
252
|
-
}
|
|
254
|
+
}
|
|
253
255
|
--position
|
|
254
|
-
endToken
|
|
256
|
+
endToken?.()
|
|
255
257
|
return undefined
|
|
256
|
-
}
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
function parseKey () {
|
|
261
262
|
let result
|
|
262
263
|
if (position < inputLength) {
|
|
263
|
-
startToken
|
|
264
|
+
startToken?.()
|
|
264
265
|
const char = input[position++]
|
|
265
266
|
if (char === '"' || (char === '\'' && allowSingleQuotedStrings)) {
|
|
266
267
|
const string = parseString(char)
|
|
267
|
-
endToken
|
|
268
|
+
endToken?.('literal', string)
|
|
268
269
|
return string
|
|
269
|
-
}
|
|
270
|
-
endToken
|
|
270
|
+
}if (char === '{') {
|
|
271
|
+
endToken?.('symbol', '{')
|
|
271
272
|
return parseObject()
|
|
272
|
-
}
|
|
273
|
-
endToken
|
|
273
|
+
}if (char === '[') {
|
|
274
|
+
endToken?.('symbol', '[')
|
|
274
275
|
return parseArray()
|
|
275
|
-
}
|
|
276
|
+
}if (char === '.' || isDecDigit(char)) {
|
|
276
277
|
const number = parseNumber(true)
|
|
277
|
-
endToken
|
|
278
|
+
endToken?.('literal', number)
|
|
278
279
|
return number
|
|
279
|
-
}
|
|
280
|
+
}if ((json5 && Uni.isIdentifierStart(char)) ||
|
|
280
281
|
(char === '\\' && input[position] === 'u')) {
|
|
281
282
|
const rollback = position - 1
|
|
282
283
|
result = parseIdentifier()
|
|
283
284
|
if (result === undefined) {
|
|
284
285
|
position = rollback
|
|
285
|
-
endToken
|
|
286
|
+
endToken?.()
|
|
286
287
|
return undefined
|
|
287
|
-
} else {
|
|
288
|
-
endToken && endToken('literal', result)
|
|
289
|
-
return result
|
|
290
288
|
}
|
|
291
|
-
|
|
289
|
+
endToken?.('literal', result)
|
|
290
|
+
return result
|
|
291
|
+
}
|
|
292
292
|
--position
|
|
293
|
-
endToken
|
|
293
|
+
endToken?.()
|
|
294
294
|
return undefined
|
|
295
|
-
}
|
|
296
295
|
}
|
|
297
296
|
}
|
|
298
297
|
|
|
299
298
|
function skipBOM () {
|
|
300
299
|
if (isBOM(input)) {
|
|
301
|
-
startToken
|
|
300
|
+
startToken?.()
|
|
302
301
|
++position
|
|
303
|
-
endToken
|
|
302
|
+
endToken?.('bom')
|
|
304
303
|
}
|
|
305
304
|
}
|
|
306
305
|
|
|
@@ -336,7 +335,7 @@ function parseInternal (input, options) {
|
|
|
336
335
|
++position
|
|
337
336
|
}
|
|
338
337
|
skipComment(input[position++] === '*')
|
|
339
|
-
endToken
|
|
338
|
+
endToken?.('comment')
|
|
340
339
|
} else {
|
|
341
340
|
--position
|
|
342
341
|
break
|
|
@@ -389,29 +388,29 @@ function parseInternal (input, options) {
|
|
|
389
388
|
while (position < inputLength) {
|
|
390
389
|
skipWhiteSpace()
|
|
391
390
|
const key = parseKey()
|
|
392
|
-
if (allowDuplicateObjectKeys === false && result
|
|
393
|
-
fail(
|
|
391
|
+
if (allowDuplicateObjectKeys === false && ownsProperty.call(result, key)) {
|
|
392
|
+
fail(`Duplicate key: "${key}"`)
|
|
394
393
|
}
|
|
395
394
|
skipWhiteSpace()
|
|
396
|
-
startToken
|
|
395
|
+
startToken?.()
|
|
397
396
|
let char = input[position++]
|
|
398
|
-
endToken
|
|
397
|
+
endToken?.('symbol', char)
|
|
399
398
|
if (char === '}' && key === undefined) {
|
|
400
399
|
if (!ignoreTrailingCommas && isNotEmpty) {
|
|
401
400
|
--position
|
|
402
401
|
fail('Trailing comma in object')
|
|
403
402
|
}
|
|
404
403
|
return result
|
|
405
|
-
}
|
|
404
|
+
}if (char === ':' && key !== undefined) {
|
|
406
405
|
skipWhiteSpace()
|
|
407
|
-
tokenPath
|
|
406
|
+
tokenPath?.push(key)
|
|
408
407
|
let value = parseGeneric()
|
|
409
|
-
tokenPath
|
|
408
|
+
tokenPath?.pop()
|
|
410
409
|
|
|
411
|
-
if (value === undefined) fail(
|
|
410
|
+
if (value === undefined) fail(`No value found for key "${key}"`)
|
|
412
411
|
if (typeof key !== 'string') {
|
|
413
412
|
if (!json5 || typeof key !== 'number') {
|
|
414
|
-
fail(
|
|
413
|
+
fail(`Wrong key type: "${key}"`)
|
|
415
414
|
}
|
|
416
415
|
}
|
|
417
416
|
|
|
@@ -428,11 +427,10 @@ function parseInternal (input, options) {
|
|
|
428
427
|
}
|
|
429
428
|
|
|
430
429
|
skipWhiteSpace()
|
|
431
|
-
startToken
|
|
430
|
+
startToken?.()
|
|
432
431
|
char = input[position++]
|
|
433
|
-
endToken
|
|
432
|
+
endToken?.('symbol', char)
|
|
434
433
|
if (char === ',') {
|
|
435
|
-
continue
|
|
436
434
|
} else if (char === '}') {
|
|
437
435
|
return result
|
|
438
436
|
} else {
|
|
@@ -451,13 +449,13 @@ function parseInternal (input, options) {
|
|
|
451
449
|
const result = []
|
|
452
450
|
while (position < inputLength) {
|
|
453
451
|
skipWhiteSpace()
|
|
454
|
-
tokenPath
|
|
452
|
+
tokenPath?.push(result.length)
|
|
455
453
|
let item = parseGeneric()
|
|
456
|
-
tokenPath
|
|
454
|
+
tokenPath?.pop()
|
|
457
455
|
skipWhiteSpace()
|
|
458
|
-
startToken
|
|
456
|
+
startToken?.()
|
|
459
457
|
const char = input[position++]
|
|
460
|
-
endToken
|
|
458
|
+
endToken?.('symbol', char)
|
|
461
459
|
if (item !== undefined) {
|
|
462
460
|
if (reviver) {
|
|
463
461
|
item = reviver(String(result.length), item)
|
|
@@ -498,18 +496,18 @@ function parseInternal (input, options) {
|
|
|
498
496
|
let result
|
|
499
497
|
|
|
500
498
|
if (isOctal) {
|
|
501
|
-
result = parseInt(string.replace(/^0o?/, ''), 8)
|
|
499
|
+
result = Number.parseInt(string.replace(/^0o?/, ''), 8)
|
|
502
500
|
} else {
|
|
503
501
|
result = Number(string)
|
|
504
502
|
}
|
|
505
503
|
|
|
506
504
|
if (Number.isNaN(result)) {
|
|
507
505
|
--position
|
|
508
|
-
fail(
|
|
506
|
+
fail(`Bad numeric literal - "${input.substr(start, position - start + 1)}"`)
|
|
509
507
|
} else if (!json5 && !string.match(/^-?(0|[1-9][0-9]*)(\.[0-9]+)?(e[+-]?[0-9]+)?$/i)) {
|
|
510
508
|
// additional restrictions imposed by json
|
|
511
509
|
--position
|
|
512
|
-
fail(
|
|
510
|
+
fail(`Non-json numeric literal - "${input.substr(start, position - start + 1)}"`)
|
|
513
511
|
} else {
|
|
514
512
|
return result
|
|
515
513
|
}
|
|
@@ -523,7 +521,7 @@ function parseInternal (input, options) {
|
|
|
523
521
|
|
|
524
522
|
if (char === 'N' && json5) {
|
|
525
523
|
parseKeyword('NaN')
|
|
526
|
-
return NaN
|
|
524
|
+
return Number.NaN
|
|
527
525
|
}
|
|
528
526
|
|
|
529
527
|
if (char === 'I' && json5) {
|
|
@@ -608,7 +606,7 @@ function parseInternal (input, options) {
|
|
|
608
606
|
isHexDigit(input[position + 3]) &&
|
|
609
607
|
isHexDigit(input[position + 4])) {
|
|
610
608
|
// UnicodeEscapeSequence
|
|
611
|
-
char = String.fromCharCode(parseInt(input.substr(position + 1, 4), 16))
|
|
609
|
+
char = String.fromCharCode(Number.parseInt(input.substr(position + 1, 4), 16))
|
|
612
610
|
position += 5
|
|
613
611
|
}
|
|
614
612
|
|
|
@@ -639,7 +637,7 @@ function parseInternal (input, options) {
|
|
|
639
637
|
let char = input[position++]
|
|
640
638
|
if (char === endChar) {
|
|
641
639
|
return result
|
|
642
|
-
}
|
|
640
|
+
}if (char === '\\') {
|
|
643
641
|
if (position >= inputLength) {
|
|
644
642
|
fail()
|
|
645
643
|
}
|
|
@@ -662,7 +660,7 @@ function parseInternal (input, options) {
|
|
|
662
660
|
}
|
|
663
661
|
position++
|
|
664
662
|
}
|
|
665
|
-
result += String.fromCharCode(parseInt(input.substr(position - count, count), 16))
|
|
663
|
+
result += String.fromCharCode(Number.parseInt(input.substr(position - count, count), 16))
|
|
666
664
|
} else if (json5 && isOctDigit(char)) {
|
|
667
665
|
let digits
|
|
668
666
|
if (char < '4' && isOctDigit(input[position]) && isOctDigit(input[position + 1])) {
|
|
@@ -675,7 +673,7 @@ function parseInternal (input, options) {
|
|
|
675
673
|
digits = 1
|
|
676
674
|
}
|
|
677
675
|
position += digits - 1
|
|
678
|
-
result += String.fromCharCode(parseInt(input.substr(position - digits, digits), 8))
|
|
676
|
+
result += String.fromCharCode(Number.parseInt(input.substr(position - digits, digits), 8))
|
|
679
677
|
} else if (json5) {
|
|
680
678
|
// \X -> x
|
|
681
679
|
result += char
|
|
@@ -710,9 +708,8 @@ function parseInternal (input, options) {
|
|
|
710
708
|
returnValue = reviver('', returnValue)
|
|
711
709
|
}
|
|
712
710
|
return tokenize ? tokens : returnValue
|
|
713
|
-
} else {
|
|
714
|
-
fail()
|
|
715
711
|
}
|
|
712
|
+
fail()
|
|
716
713
|
} else {
|
|
717
714
|
if (position) {
|
|
718
715
|
fail('No data, only a whitespace')
|
|
@@ -759,9 +756,9 @@ function pathToPointer (tokens) {
|
|
|
759
756
|
if (tokens.length === 0) {
|
|
760
757
|
return ''
|
|
761
758
|
}
|
|
762
|
-
return
|
|
759
|
+
return `/${tokens
|
|
763
760
|
.map(escapePointerToken)
|
|
764
|
-
.join('/')
|
|
761
|
+
.join('/')}`
|
|
765
762
|
}
|
|
766
763
|
|
|
767
764
|
function unescapePointerToken (token) {
|
|
@@ -825,7 +822,7 @@ function upcomingInput (input, offset) {
|
|
|
825
822
|
function getPositionContext (input, offset) {
|
|
826
823
|
const past = pastInput(input, offset)
|
|
827
824
|
const upcoming = upcomingInput(input, offset)
|
|
828
|
-
const pointer = new Array(past.length + 1).join('-')
|
|
825
|
+
const pointer = `${new Array(past.length + 1).join('-')}^`
|
|
829
826
|
return {
|
|
830
827
|
excerpt: past + upcoming,
|
|
831
828
|
pointer
|
|
@@ -889,13 +886,13 @@ function getLocationOnSpiderMonkey (input, reason) {
|
|
|
889
886
|
function getTexts (reason, input, offset, line, column) {
|
|
890
887
|
const position = getPositionContext(input, offset)
|
|
891
888
|
const excerpt = position.excerpt
|
|
892
|
-
let message
|
|
889
|
+
let message
|
|
890
|
+
let pointer
|
|
893
891
|
if (typeof line === 'number') {
|
|
894
892
|
pointer = position.pointer
|
|
895
|
-
message =
|
|
896
|
-
column + ':\n' + excerpt + '\n' + pointer + '\n' + reason
|
|
893
|
+
message = `Parse error on line ${line}, column ${column}:\n${excerpt}\n${pointer}\n${reason}`
|
|
897
894
|
} else {
|
|
898
|
-
message =
|
|
895
|
+
message = `Parse error in JSON input:\n${excerpt}\n${reason}`
|
|
899
896
|
}
|
|
900
897
|
return {
|
|
901
898
|
message,
|
|
@@ -909,7 +906,9 @@ function improveNativeError (input, error) {
|
|
|
909
906
|
const location = getLocationOnV8(input, reason) ||
|
|
910
907
|
checkUnexpectedEndOnV8(input, reason) ||
|
|
911
908
|
getLocationOnSpiderMonkey(input, reason)
|
|
912
|
-
let offset
|
|
909
|
+
let offset
|
|
910
|
+
let line
|
|
911
|
+
let column
|
|
913
912
|
if (location) {
|
|
914
913
|
offset = location.offset
|
|
915
914
|
line = location.line
|
|
@@ -940,7 +939,11 @@ function parseNative (input, reviver) {
|
|
|
940
939
|
try {
|
|
941
940
|
return JSON.parse(input, reviver)
|
|
942
941
|
} catch (error) {
|
|
943
|
-
|
|
942
|
+
const newError = improveNativeError(input, error)
|
|
943
|
+
if (error.location) throw newError
|
|
944
|
+
// If the native error didn't contain location, parse once more
|
|
945
|
+
// by the custom parser, which always provides the error location.
|
|
946
|
+
return parseCustom (input, reviver)
|
|
944
947
|
}
|
|
945
948
|
}
|
|
946
949
|
/* globals navigator, process, parseCustom, parseNative */
|
|
@@ -957,7 +960,7 @@ function needsCustomParser (options) {
|
|
|
957
960
|
function getReviver (options) {
|
|
958
961
|
if (typeof options === 'function') {
|
|
959
962
|
return options
|
|
960
|
-
}
|
|
963
|
+
}if (options) {
|
|
961
964
|
return options.reviver
|
|
962
965
|
}
|
|
963
966
|
}
|