Skip to content

Commit f46dc83

Browse files
committed
Better errors when the token is a non-printable control char
I had a heck of a time trying to debug a bit of JSON that had an extra byte-order mark char in it while testing the previous commit. This makes the token print out with JSON.stringify, and includes the char code in hex, so it's easier to tell why it's a problem.
1 parent 4dd9054 commit f46dc83

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

index.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
'use strict'
22

3+
const hexify = char => {
4+
const h = char.charCodeAt(0).toString(16).toUpperCase()
5+
return '0x' + (h.length % 2 ? '0' : '') + h
6+
}
7+
38
const parseError = (e, txt, context) => {
49
if (!txt) {
510
return {
611
message: e.message + ' while parsing empty string',
712
position: 0,
813
}
914
}
10-
const badToken = e.message.match(/^Unexpected token.*position\s+(\d+)/i)
11-
const errIdx = badToken ? +badToken[1]
15+
const badToken = e.message.match(/^Unexpected token (.) .*position\s+(\d+)/i)
16+
const errIdx = badToken ? +badToken[2]
1217
: e.message.match(/^Unexpected end of JSON.*/i) ? txt.length - 1
1318
: null
1419

20+
const msg = badToken ? e.message.replace(/^Unexpected token ./, `Unexpected token ${
21+
JSON.stringify(badToken[1])
22+
} (${hexify(badToken[1])})`)
23+
: e.message
24+
1525
if (errIdx !== null && errIdx !== undefined) {
1626
const start = errIdx <= context ? 0
1727
: errIdx - context
@@ -26,12 +36,12 @@ const parseError = (e, txt, context) => {
2636
const near = txt === slice ? '' : 'near '
2737

2838
return {
29-
message: e.message + ` while parsing ${near}'${slice}'`,
39+
message: msg + ` while parsing ${near}${JSON.stringify(slice)}`,
3040
position: errIdx,
3141
}
3242
} else {
3343
return {
34-
message: e.message + ` while parsing '${txt.slice(0, context * 2)}'`,
44+
message: msg + ` while parsing '${txt.slice(0, context * 2)}'`,
3545
position: 0,
3646
}
3747
}

test/index.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,31 @@ t.test('parses JSON if it is a Buffer, removing BOM bytes', t => {
2929
t.end()
3030
})
3131

32+
t.test('better errors when faced with \\b and other malarky', t => {
33+
const str = JSON.stringify({
34+
foo: 1,
35+
bar: {
36+
baz: [1, 2, 3, 'four']
37+
}
38+
})
39+
const data = Buffer.from(str)
40+
const bombom = Buffer.concat([Buffer.from([0xEF, 0xBB, 0xBF, 0xEF, 0xBB, 0xBF]), data])
41+
t.throws(() => parseJson(bombom), {
42+
message: /\(0xFEFF\) in JSON at position 0/
43+
}, 'only strips a single BOM, not multiple')
44+
const bs = str + '\b\b\b\b\b\b\b\b\b\b\b\b'
45+
t.throws(() => parseJson(bs), {
46+
message: /^Unexpected token "\\b" \(0x08\) in JSON at position.*\\b"$/
47+
})
48+
t.end()
49+
})
50+
3251
t.test('throws SyntaxError for unexpected token', t => {
3352
const data = 'foo'
3453
t.throws(
3554
() => parseJson(data),
3655
{
37-
message: 'Unexpected token o in JSON at position 1 while parsing \'foo\'',
56+
message: 'Unexpected token "o" (0x6F) in JSON at position 1 while parsing "foo"',
3857
code: 'EJSONPARSE',
3958
position: 1,
4059
name: 'JSONParseError',
@@ -49,7 +68,7 @@ t.test('throws SyntaxError for unexpected end of JSON', t => {
4968
t.throws(
5069
() => parseJson(data),
5170
{
52-
message: 'Unexpected end of JSON input while parsing \'{"foo: bar}\'',
71+
message: 'Unexpected end of JSON input while parsing "{\\\"foo: bar}"',
5372
code: 'EJSONPARSE',
5473
position: 10,
5574
name: 'JSONParseError',
@@ -79,7 +98,7 @@ t.test('SyntaxError with less context (limited start)', t => {
7998
t.throws(
8099
() => parseJson(data, null, 3),
81100
{
82-
message: 'Unexpected end of JSON input while parsing near \'...3210\'',
101+
message: 'Unexpected end of JSON input while parsing near "...3210"',
83102
code: 'EJSONPARSE',
84103
position: 8,
85104
name: 'JSONParseError',
@@ -93,7 +112,7 @@ t.test('SyntaxError with less context (limited end)', t => {
93112
t.throws(
94113
() => parseJson(data, null, 2),
95114
{
96-
message: 'Unexpected token a in JSON at position 0 while parsing near \'ab...\'',
115+
message: 'Unexpected token "a" \(0x61\) in JSON at position 0 while parsing near "ab..."',
97116
code: 'EJSONPARSE',
98117
position: 0,
99118
name: 'JSONParseError',

0 commit comments

Comments
 (0)