quick-json5 0.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.md +7 -0
- package/README.md +66 -0
- package/dist/exports/Exports.d.ts +6 -0
- package/dist/exports/Exports.d.ts.map +1 -0
- package/dist/exports/Exports.js +6 -0
- package/dist/exports/Exports.js.map +1 -0
- package/dist/json/JsonEncoder.d.ts +3 -0
- package/dist/json/JsonEncoder.d.ts.map +1 -0
- package/dist/json/JsonEncoder.js +147 -0
- package/dist/json/JsonEncoder.js.map +1 -0
- package/dist/json/JsonParser.d.ts +3 -0
- package/dist/json/JsonParser.d.ts.map +1 -0
- package/dist/json/JsonParser.js +378 -0
- package/dist/json/JsonParser.js.map +1 -0
- package/dist/json5/Json5Encoder.d.ts +3 -0
- package/dist/json5/Json5Encoder.d.ts.map +1 -0
- package/dist/json5/Json5Encoder.js +155 -0
- package/dist/json5/Json5Encoder.js.map +1 -0
- package/dist/json5/Json5Parser.d.ts +3 -0
- package/dist/json5/Json5Parser.d.ts.map +1 -0
- package/dist/json5/Json5Parser.js +781 -0
- package/dist/json5/Json5Parser.js.map +1 -0
- package/dist/tests/Json5EncoderTest.d.ts +2 -0
- package/dist/tests/Json5EncoderTest.d.ts.map +1 -0
- package/dist/tests/Json5EncoderTest.js +44 -0
- package/dist/tests/Json5EncoderTest.js.map +1 -0
- package/dist/tests/Json5ParserTests.d.ts +3 -0
- package/dist/tests/Json5ParserTests.d.ts.map +1 -0
- package/dist/tests/Json5ParserTests.js +211 -0
- package/dist/tests/Json5ParserTests.js.map +1 -0
- package/dist/tests/JsonEncoderTests.d.ts +2 -0
- package/dist/tests/JsonEncoderTests.d.ts.map +1 -0
- package/dist/tests/JsonEncoderTests.js +44 -0
- package/dist/tests/JsonEncoderTests.js.map +1 -0
- package/dist/tests/JsonParserTests.d.ts +3 -0
- package/dist/tests/JsonParserTests.d.ts.map +1 -0
- package/dist/tests/JsonParserTests.js +140 -0
- package/dist/tests/JsonParserTests.js.map +1 -0
- package/dist/tests/Test.d.ts +2 -0
- package/dist/tests/Test.d.ts.map +1 -0
- package/dist/tests/Test.js +9 -0
- package/dist/tests/Test.js.map +1 -0
- package/dist/types/Types.d.ts +8 -0
- package/dist/types/Types.d.ts.map +1 -0
- package/dist/types/Types.js +2 -0
- package/dist/types/Types.js.map +1 -0
- package/dist/unused/Unused.d.ts +2 -0
- package/dist/unused/Unused.d.ts.map +1 -0
- package/dist/unused/Unused.js +47 -0
- package/dist/unused/Unused.js.map +1 -0
- package/dist/utilities/JsonParseError.d.ts +7 -0
- package/dist/utilities/JsonParseError.d.ts.map +1 -0
- package/dist/utilities/JsonParseError.js +10 -0
- package/dist/utilities/JsonParseError.js.map +1 -0
- package/dist/utilities/ObjectUtilities.d.ts +2 -0
- package/dist/utilities/ObjectUtilities.d.ts.map +1 -0
- package/dist/utilities/ObjectUtilities.js +142 -0
- package/dist/utilities/ObjectUtilities.js.map +1 -0
- package/dist/utilities/TypedArray.d.ts +4 -0
- package/dist/utilities/TypedArray.d.ts.map +1 -0
- package/dist/utilities/TypedArray.js +2 -0
- package/dist/utilities/TypedArray.js.map +1 -0
- package/dist/utilities/Utilities.d.ts +17 -0
- package/dist/utilities/Utilities.d.ts.map +1 -0
- package/dist/utilities/Utilities.js +97 -0
- package/dist/utilities/Utilities.js.map +1 -0
- package/package.json +38 -0
- package/src/exports/Exports.ts +7 -0
- package/src/json/JsonEncoder.ts +180 -0
- package/src/json/JsonParser.ts +491 -0
- package/src/json5/Json5Encoder.ts +189 -0
- package/src/json5/Json5Parser.ts +959 -0
- package/src/tests/Json5EncoderTest.ts +51 -0
- package/src/tests/Json5ParserTests.ts +237 -0
- package/src/tests/JsonEncoderTests.ts +51 -0
- package/src/tests/JsonParserTests.ts +159 -0
- package/src/tests/Test.ts +13 -0
- package/src/types/Types.ts +9 -0
- package/src/unused/Unused.ts +51 -0
- package/src/utilities/JsonParseError.ts +7 -0
- package/src/utilities/ObjectUtilities.ts +184 -0
- package/src/utilities/TypedArray.ts +39 -0
- package/src/utilities/Utilities.ts +130 -0
- package/tsconfig.json +55 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { stringify } from '../json5/Json5Encoder.js'
|
|
2
|
+
|
|
3
|
+
const print = console.log
|
|
4
|
+
|
|
5
|
+
export function testJson5Encoder() {
|
|
6
|
+
const obj1 = {
|
|
7
|
+
hi: 123,
|
|
8
|
+
there: [
|
|
9
|
+
1,
|
|
10
|
+
2,
|
|
11
|
+
3
|
|
12
|
+
],
|
|
13
|
+
yo: {
|
|
14
|
+
boom: 'asdf',
|
|
15
|
+
ba: {
|
|
16
|
+
go: 65
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const obj2 = [
|
|
22
|
+
{
|
|
23
|
+
hi: 123,
|
|
24
|
+
there: [
|
|
25
|
+
1,
|
|
26
|
+
2,
|
|
27
|
+
3
|
|
28
|
+
],
|
|
29
|
+
yo: {
|
|
30
|
+
boom: 'asdf',
|
|
31
|
+
ba: {
|
|
32
|
+
go: 65
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const obj = obj2
|
|
39
|
+
|
|
40
|
+
const replacer = undefined
|
|
41
|
+
const space = 4
|
|
42
|
+
|
|
43
|
+
const reference = JSON.stringify(obj, replacer, space)
|
|
44
|
+
const result = stringify(obj, replacer, space)
|
|
45
|
+
|
|
46
|
+
print('JSON.stringify:')
|
|
47
|
+
print(reference)
|
|
48
|
+
|
|
49
|
+
print('\n\nencodeJson:')
|
|
50
|
+
print(result)
|
|
51
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { parse } from '../json5/Json5Parser.js'
|
|
2
|
+
import { deepEquals } from '../utilities/ObjectUtilities.js'
|
|
3
|
+
|
|
4
|
+
const log = console.log
|
|
5
|
+
|
|
6
|
+
export function testJson5Parser() {
|
|
7
|
+
for (const [source, expected] of testPairs) {
|
|
8
|
+
let parsedObj: unknown
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
parsedObj = parse(source, undefined, { enableExtensions: true })
|
|
12
|
+
} catch (e: any) {
|
|
13
|
+
log(`Error when parsing:`)
|
|
14
|
+
log(source)
|
|
15
|
+
log(``)
|
|
16
|
+
log(e.message)
|
|
17
|
+
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!deepEquals(parsedObj, expected)) {
|
|
22
|
+
log(`JSON5 not parsed correctly:`)
|
|
23
|
+
log(source)
|
|
24
|
+
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function testJson5ParserInvalidInputs() {
|
|
31
|
+
for (const invalidInput of invalidInputs) {
|
|
32
|
+
let parsedObj: any
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
parsedObj = parse(invalidInput)
|
|
36
|
+
} catch (e: any) {
|
|
37
|
+
log(`Error when parsing:`)
|
|
38
|
+
log(invalidInput)
|
|
39
|
+
log(``)
|
|
40
|
+
log(e.message)
|
|
41
|
+
log(``)
|
|
42
|
+
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
throw new Error(`No error when parsing ${invalidInput}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const testPairs: [string, unknown][] = [
|
|
51
|
+
['{}', {}],
|
|
52
|
+
['{"a":1}', { a: 1 }],
|
|
53
|
+
["{'a':1}", { a: 1 }],
|
|
54
|
+
['{a:1}', { a: 1 }],
|
|
55
|
+
['{a\\u200C:3}', { 'a\u200C': 3 }],
|
|
56
|
+
['{$_:1,_$:2,a\\u200C:3}', { $_: 1, _$: 2, 'a\u200C': 3 }],
|
|
57
|
+
['{ùńîċõďë:9}', { 'ùńîċõďë': 9 }],
|
|
58
|
+
['{\\u0061\\u0062:1,\\u0024\\u005F:2,\\u005F\\u0024:3}', { ab: 1, $_: 2, _$: 3 }],
|
|
59
|
+
['{"__proto__":1}', { "__proto__": 1 }],
|
|
60
|
+
['{abc:1,def:2}', { abc: 1, def: 2 }],
|
|
61
|
+
['{a:{b:2}}', { a: { b: 2 } }],
|
|
62
|
+
['[12341234123412341234n, "asdf"]', [12341234123412341234n, 'asdf']],
|
|
63
|
+
|
|
64
|
+
['[]', []],
|
|
65
|
+
['[1]', [1]],
|
|
66
|
+
['[1,2]', [1, 2]],
|
|
67
|
+
['[1,[2,3]]', [1, [2, 3]]],
|
|
68
|
+
['[[]]', [[]]],
|
|
69
|
+
['[[[],[[[[1, false, null, "hi"]]]]]]', [[[], [[[[1, false, null, "hi"]]]]]]],
|
|
70
|
+
|
|
71
|
+
['null', null],
|
|
72
|
+
|
|
73
|
+
['true', true],
|
|
74
|
+
['false', false],
|
|
75
|
+
|
|
76
|
+
['[0,0.,0e0]', [0, 0, 0]],
|
|
77
|
+
['[1,23,456,7890]', [1, 23, 456, 7890]],
|
|
78
|
+
['[-1,+2,-.1,-0]', [-1, 2, -0.1, -0]],
|
|
79
|
+
['[.1,.23]', [0.1, 0.23]],
|
|
80
|
+
['[1.0,1.23]', [1, 1.23]],
|
|
81
|
+
['[1e0,1e1,1e01,1.e0,1.1e0,1e-1,1e+1]', [1, 10, 10, 1, 1.1, 0.1, 10]],
|
|
82
|
+
['[0x1,0x10,0xff,0xFF]', [1, 16, 255, 255]],
|
|
83
|
+
['[Infinity,-Infinity]', [Infinity, -Infinity]],
|
|
84
|
+
['NaN', NaN],
|
|
85
|
+
['-NaN', NaN],
|
|
86
|
+
['1', 1],
|
|
87
|
+
['+1.23e100', 1.23e+100],
|
|
88
|
+
['+1.23e-100', 1.23e-100],
|
|
89
|
+
['0x1', 0x1],
|
|
90
|
+
['+0x01b23A4', +0x01b23A4],
|
|
91
|
+
['-0x0123456789abcdefABCDEF', -0x0123456789abcdefABCDEF],
|
|
92
|
+
|
|
93
|
+
['"abc"', 'abc'],
|
|
94
|
+
["'abc'", 'abc'],
|
|
95
|
+
[`['"', "'"]`, ['"', "'"]],
|
|
96
|
+
[`['', ""]`, ['', '']],
|
|
97
|
+
|
|
98
|
+
['{//hey\r\n}', {}],
|
|
99
|
+
['{//hey\r\n x: 1234 //yo\n}', { x: 1234 }],
|
|
100
|
+
['{ /*Comment!\n\r\n*/ }', {}],
|
|
101
|
+
['{ /*Comment 1!\n\r\n*/ x: 1234 /* Comment 2! */ }', { x: 1234 }],
|
|
102
|
+
|
|
103
|
+
['{\t\v\f \u00A0\uFEFF\n\r\u2028\u2029\u2003}', {}],
|
|
104
|
+
|
|
105
|
+
[`'\\u01fF'`, `\u01fF`],
|
|
106
|
+
[`'abc\\u01fFabc'`, `abc\u01fFabc`],
|
|
107
|
+
[`'\\x12'`, `\x12`],
|
|
108
|
+
[`'abc\\x12abc'`, `abc\x12abc`],
|
|
109
|
+
[`'😅hey🙃'`, `😅hey🙃`],
|
|
110
|
+
[`'\\u01fF\\\n\\\r\n\\\r\\\u2028\\\u2029\\a\\'\\"'`, `\u01FF\a'"`],
|
|
111
|
+
[`'a\\\nb'`, 'ab'],
|
|
112
|
+
[`'a\\\r\nb'`, 'ab'],
|
|
113
|
+
[`"a\\\u2028b"`, 'ab'],
|
|
114
|
+
[`'a\\\u2029b'`, 'ab'],
|
|
115
|
+
[`'\\b\\f\\n\\r\\t\\v\\0\\x0f\\u01fF\\\n\\\r\n\\\r\\\u2028\\\u2029\\a\\'\\"'`, `\b\f\n\r\t\v\0\x0f\u01FF\a'"`],
|
|
116
|
+
[`'hey \u2028 p \u2029 there'`, `hey \u2028 p \u2029 there`], // \u2028 and \u2029 can be used in string
|
|
117
|
+
[`'hey \\\u2028\\\u2029 there'`, `hey there`], // \u2028 and \u2029 can be used for line continuation
|
|
118
|
+
['"unicode \\u263A"', 'unicode ☺'], // unicode escape sequence
|
|
119
|
+
['"surrogate pair: \\uD834\\uDD1E"', 'surrogate pair: 𝄞'], // surrogate pair unicode
|
|
120
|
+
|
|
121
|
+
[' { } ', {}], // Whitespace around object
|
|
122
|
+
[' [ ] ', []], // Whitespace around array
|
|
123
|
+
[' { "a" : 1 } ', { a: 1 }], // Whitespace around key-value colon
|
|
124
|
+
[' { "a" : 1 , } ', { a: 1 }], // Whitespace around trailing comma
|
|
125
|
+
[' [ 1 , 2 ] ', [1, 2]], // Whitespace around array commas
|
|
126
|
+
[' " abc " ', ' abc '], // Whitespace inside string
|
|
127
|
+
[' \t\r\n{\t\r\n"a"\t\r\n:\t\r\n1\t\r\n}\t\r\n ', { a: 1 }], // Extensive whitespace everywhere
|
|
128
|
+
['\u00A0{\u2003}\uFEFF', {}], // Unicode whitespace characters
|
|
129
|
+
['\u000B{\u000C}', {}], // More whitespace characters (vertical tab, form feed)
|
|
130
|
+
|
|
131
|
+
['// comment\n{}', {}], // Comment before object
|
|
132
|
+
['{}// comment', {}], // Comment after object
|
|
133
|
+
['[] // comment', []], // Comment after array
|
|
134
|
+
['// comment\n[]', []], // Comment before array
|
|
135
|
+
['{\n // comment\n "a": 1\n}', { a: 1 }], // Comment inside object - before key-value
|
|
136
|
+
['{\n "a": // comment\n 1\n}', { a: 1 }], // Comment inside object - between key and value
|
|
137
|
+
[`{\n 'a': 1 // comment\n}`, { a: 1 }], // Comment inside object - after value
|
|
138
|
+
['[\n // comment\n 1,\n 2\n]', [1, 2]], // Comment inside array - before element
|
|
139
|
+
['[\n 1, // comment\n 2\n]', [1, 2]], // Comment inside array - between elements
|
|
140
|
+
['[\n 1,\n 2 // comment\n]', [1, 2]], // Comment inside array - after element
|
|
141
|
+
['/* multi-line comment */ {}', {}], // Multi-line comment before object
|
|
142
|
+
['{} /* multi-line comment */', {}], // Multi-line comment after object
|
|
143
|
+
['/*\nmulti\nline\ncomment\n*/ {}', {}], // Multi-line comment with newlines
|
|
144
|
+
['{\n /* multi-line\n comment */\n "a": 1\n}', { a: 1 }], // Multi-line comment inside object
|
|
145
|
+
['{\n "a": 1, /*\n multi-line\n comment */\n}', { a: 1 }], // Multi-line comment at the end of object
|
|
146
|
+
['// single-line comment\n{ "a": 1 } /* multi-line comment */ // another single-line', { a: 1 }], // Mixed comments
|
|
147
|
+
['{\n "a": 1, // single-line comment\n "b": 2 /* multi-line comment */\n}', { a: 1, b: 2 }], // Comments interspersed in object
|
|
148
|
+
['/* comment before */{/* comment inside object */}/* comment after */', {}], // Comments around and inside object
|
|
149
|
+
['/* comment before */[/* comment inside array */]/* comment after */', []], // Comments around and inside array
|
|
150
|
+
|
|
151
|
+
['{ "a": 1, }', { a: 1 }], // Trailing comma in object
|
|
152
|
+
['[ 1, 2, ]', [1, 2]], // Trailing comma in array
|
|
153
|
+
['{ "a": 1 ,}', { a: 1 }], // Trailing comma with whitespace
|
|
154
|
+
['[ 1, 2 ,]', [1, 2]], // Trailing comma with whitespace in array
|
|
155
|
+
['{ "a": 1 , // comment\n}', { a: 1 }], // Trailing comma with comment
|
|
156
|
+
['[ 1, 2 , // comment\n]', [1, 2]], // Trailing comma with comment in array
|
|
157
|
+
['{ ùńîċõďë: 1 , \t\r\n}', { ùńîċõďë: 1 }], // Trailing comma with various whitespace
|
|
158
|
+
['[ 1, 2 , \t\r\n]', [1, 2]], // Trailing comma with various whitespace in array
|
|
159
|
+
['[ 1, 2 \t\r\n , \t\r\n]', [1, 2]], // Trailing comma with various whitespace before and after it
|
|
160
|
+
['{ "a": 1, /* comment */ }', { a: 1 }], // Trailing comma position, but no comma - still valid
|
|
161
|
+
['[ 1, 2, /* comment */ ]', [1, 2]], // Trailing comma position, but no comma in array - still valid
|
|
162
|
+
[`{ '😅hey🙃': 123 /* comment */, }`, { '😅hey🙃': 123 }], // Emojis in key
|
|
163
|
+
|
|
164
|
+
['[0.0, 0.00, 0.000]', [0, 0, 0]], // Zero with multiple decimal places
|
|
165
|
+
['[12345678901234567890]', [12345678901234567890]], // Large integer
|
|
166
|
+
['[-12345678901234567890]', [-12345678901234567890]], // Large negative integer
|
|
167
|
+
['[1e+100, 1e-100, 1e100]', [1e+100, 1e-100, 1e+100]], // Large and small exponents
|
|
168
|
+
['[1.23456789e+100, 1.23456789e-100]', [1.23456789e+100, 1.23456789e-100]], // Decimal with large/small exponents
|
|
169
|
+
['[0x0, 0x9, 0xa, 0xf, 0xA, 0xF]', [0, 9, 10, 15, 10, 15]], // Hex digits (mixed case)
|
|
170
|
+
['[0x1234567890abcdef]', [0x1234567890abcdef]], // Long hex number
|
|
171
|
+
['[-0x1234567890abcdef]', [-0x1234567890abcdef]], // Negative long hex number
|
|
172
|
+
['[+Infinity, -Infinity, NaN]', [Infinity, -Infinity, NaN]], // Explicit signs for Infinity and NaN
|
|
173
|
+
['[+NaN]', [NaN]], // Plus NaN
|
|
174
|
+
['[0., 1., 10., 100.]', [0, 1, 10, 100]], // Trailing decimal points
|
|
175
|
+
['[.0, .1, .123]', [0, 0.1, 0.123]], // Leading decimal points
|
|
176
|
+
['[123e0, 123e+0, 123e-0]', [123, 123, 123]], // Exponent with zero sign
|
|
177
|
+
['[123E0, 123E+0, 123E-0]', [123, 123, 123]], // Uppercase E exponent
|
|
178
|
+
['[123.456e7, 123.456E7]', [123.456e7, 123.456e7]], // Decimal and exponent
|
|
179
|
+
['[123.e7, 123.E7]', [123.e7, 123.e7]], // Decimal point before exponent
|
|
180
|
+
['[+0, -0]', [+0, -0]], // Positive and negative zero (important distinction in JS)
|
|
181
|
+
['[0e+10]', [0]], // Zero with exponent
|
|
182
|
+
['[18446744073709551616]', [18446744073709552000]], // Number larger than MAX_SAFE_INTEGER - might lose precision
|
|
183
|
+
['[-18446744073709551616]', [-18446744073709552000]], // Negative number larger than MAX_SAFE_INTEGER
|
|
184
|
+
['[5e-324]', [5e-324]], // Smallest positive number
|
|
185
|
+
['[-5e-324]', [-5e-324]], // Smallest negative number
|
|
186
|
+
|
|
187
|
+
[`{ identifierKey: 1 }`, { identifierKey: 1 }], // Identifier key
|
|
188
|
+
[`{ $identifier: 1 }`, { $identifier: 1 }], // Identifier starting with $
|
|
189
|
+
[`{ _identifier: 1 }`, { _identifier: 1 }], // Identifier starting with _
|
|
190
|
+
[`{ identifier_123: 1 }`, { identifier_123: 1 }], // Identifier with numbers and underscore
|
|
191
|
+
[`{ unicodeIdentifier\u200C: 1 }`, { 'unicodeIdentifier\u200C': 1 }], // Unicode identifier
|
|
192
|
+
[`{ "stringKey": 1 }`, { stringKey: 1 }], // String key (already in original tests, but good to keep)
|
|
193
|
+
[`{ "": 1 }`, { "": 1 }], // Empty string key
|
|
194
|
+
[`{ "key with spaces": 1 }`, { 'key with spaces': 1 }], // String key with spaces
|
|
195
|
+
[`{ 'key.with.dots': 1 }`, { 'key.with.dots': 1 }], // String key with dots
|
|
196
|
+
[`{ "key-with-hyphens": 1 }`, { 'key-with-hyphens': 1 }], // String key with hyphens
|
|
197
|
+
[`{ '123key': 1 }`, { '123key': 1 }], // String key starting with number
|
|
198
|
+
[`{ "key with unicode \u200C": 1 }`, { 'key with unicode \u200C': 1 }], // String key with unicode
|
|
199
|
+
[`{ key1: 1, "key1": 2 }`, { key1: 2 }], // Duplicate keys - last one wins (common behavior, but spec allows other interpretations)
|
|
200
|
+
[`{ __proto__: 1 }`, { __proto__: 1 }], // "__proto__" key - important for security considerations in JS environments
|
|
201
|
+
[`{ constructor: 1 }`, { constructor: 1 }], // "constructor" key - also relevant for JS security
|
|
202
|
+
[`{ 'hasOwnProperty': 1 }`, { 'hasOwnProperty': 1 }], // "hasOwnProperty" key - another JS object property
|
|
203
|
+
|
|
204
|
+
['{ // comment\n "a" : /* comment */ 1 , // comment\n }', { a: 1 }], // Comments and whitespace around key-value and trailing comma
|
|
205
|
+
['[ /* comment */ 1 , // comment\n 2 , /* comment */ ]', [1, 2]], // Comments and whitespace in array with trailing comma
|
|
206
|
+
['{ a /* comment */ : /* comment */ 1 }', { a: 1 }], // Comments inside key-value pair
|
|
207
|
+
['[ 1 /* comment */ , /* comment */ 2 ]', [1, 2]], // Comments inside array elements
|
|
208
|
+
['{ "a" : /* comment */ [\n 1,\r\n 2,\n ] }', { a: [1, 2] }], // Comment inside nested structure
|
|
209
|
+
['/* comment */ { /* comment */ "a" /* comment */ : /* comment */ 1 /* comment */ } /* comment */', { a: 1 }], // Comments everywhere
|
|
210
|
+
['\t\r\n// comment\n/* multi-line */\u00A0{\u2003"a":1,\n} \uFEFF', { a: 1 }], // Extreme whitespace and comments
|
|
211
|
+
['{ a: +1.23e10 // comment \n, }', { a: 1.23e+10 }], // Number with explicit plus and exponent, comment and trailing comma
|
|
212
|
+
['[ 0xdecaf, 0Xdecaf, "string\\\ncontinuation" , ]', [0xdecaf, 0xdecaf, 'stringcontinuation']], // Mixed number type, string continuation, trailing comma in array
|
|
213
|
+
|
|
214
|
+
// Extensions to JSON5:
|
|
215
|
+
// Octal literals, binary literals, big integers, underscore separators
|
|
216
|
+
['[0o1234, 0b01000101, 12341234123412341234n]', [0o1234, 0b01000101, 12341234123412341234n]], // Zero with multiple decimal places
|
|
217
|
+
['[0x1234_abc_1234_abc, 0o1234_1234, 0b01_0001_01, 1234_1234123_4123412_34n]', [0x1234_abc_1234_abc, 0o1234_1234, 0b01_0001_01, 1234_1234123_4123412_34n]], // Zero with multiple decimal places
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
const invalidInputs: string[] = [
|
|
221
|
+
// === Invalid JSON (should fail strict parsers) ===
|
|
222
|
+
'[1,2,3 trailing]', // garbage after array
|
|
223
|
+
'{ "a": undefined }', // undefined literal
|
|
224
|
+
'1,2,,3]', // double comma
|
|
225
|
+
'{,}', // comma without key-value
|
|
226
|
+
'[}', // mismatched brackets
|
|
227
|
+
'{"a":}', // missing value
|
|
228
|
+
'{"a":1 "b":2}', // missing comma between pairs
|
|
229
|
+
'{"a":1,, "b":2}', // double comma
|
|
230
|
+
'{"a":1, "b":2 "c":3}', // missing comma
|
|
231
|
+
'{"a":1 "b":2}', // missing comma
|
|
232
|
+
|
|
233
|
+
'"multi\nline"', // literal newline in string (invalid JSON, should fail)
|
|
234
|
+
'[1,,2]', // sparse array (non-standard, should fail in strict JSON parsers)
|
|
235
|
+
|
|
236
|
+
'012341234n'
|
|
237
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { stringifyJSON as stringifyToJson } from '../json/JsonEncoder.js'
|
|
2
|
+
|
|
3
|
+
const print = console.log
|
|
4
|
+
|
|
5
|
+
export function testJsonEncoder() {
|
|
6
|
+
const obj1 = {
|
|
7
|
+
hi: 123,
|
|
8
|
+
there: [
|
|
9
|
+
1,
|
|
10
|
+
2,
|
|
11
|
+
3
|
|
12
|
+
],
|
|
13
|
+
yo: {
|
|
14
|
+
boom: 'asdf',
|
|
15
|
+
ba: {
|
|
16
|
+
go: 65
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const obj2 = [
|
|
22
|
+
{
|
|
23
|
+
hi: 123,
|
|
24
|
+
there: [
|
|
25
|
+
1,
|
|
26
|
+
2,
|
|
27
|
+
3
|
|
28
|
+
],
|
|
29
|
+
yo: {
|
|
30
|
+
boom: 'asdf',
|
|
31
|
+
ba: {
|
|
32
|
+
go: 65
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const obj = obj2
|
|
39
|
+
|
|
40
|
+
const replacer = undefined
|
|
41
|
+
const space = 4
|
|
42
|
+
|
|
43
|
+
const reference = JSON.stringify(obj, replacer, space)
|
|
44
|
+
const result = stringifyToJson(obj, replacer, space)
|
|
45
|
+
|
|
46
|
+
print('JSON.stringify:')
|
|
47
|
+
print(reference)
|
|
48
|
+
|
|
49
|
+
print('\n\nencodeJson:')
|
|
50
|
+
print(result)
|
|
51
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { parseJSON } from '../json/JsonParser.js'
|
|
2
|
+
import { deepEquals } from '../utilities/ObjectUtilities.js'
|
|
3
|
+
|
|
4
|
+
const log = console.log
|
|
5
|
+
|
|
6
|
+
export function testJsonParser() {
|
|
7
|
+
for (const [source, expected] of testPairs) {
|
|
8
|
+
let parsedObj: any
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
parsedObj = parseJSON(source)
|
|
12
|
+
} catch (e: any) {
|
|
13
|
+
log(`Error when parsing:`)
|
|
14
|
+
log(source)
|
|
15
|
+
log(``)
|
|
16
|
+
log(e.message)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!deepEquals(parsedObj, expected)) {
|
|
20
|
+
log(`JSON not parsed correctly:`)
|
|
21
|
+
log(source)
|
|
22
|
+
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function testJsonParserInvalidInputs() {
|
|
29
|
+
for (const invalidInput of invalidInputs) {
|
|
30
|
+
let parsedObj: any
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
parsedObj = parseJSON(invalidInput)
|
|
34
|
+
} catch (e: any) {
|
|
35
|
+
log(`Error when parsing:`)
|
|
36
|
+
log(invalidInput)
|
|
37
|
+
log(``)
|
|
38
|
+
log(e.message)
|
|
39
|
+
log(``)
|
|
40
|
+
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error(`No error when parsing:\n ${invalidInput}\n`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const testPairs: [string, unknown][] = [
|
|
50
|
+
['[1,2]', [1, 2]], // multiple elements
|
|
51
|
+
|
|
52
|
+
// === String literals ===
|
|
53
|
+
['"x"', 'x'], // simple string
|
|
54
|
+
['""', ''], // empty string
|
|
55
|
+
['"hello world"', 'hello world'], // basic string with spaces
|
|
56
|
+
['"hello \\"world\\""', 'hello "world"'], // escaped quotes inside string
|
|
57
|
+
['"line\\nbreak"', 'line\nbreak'], // escaped newline
|
|
58
|
+
['"tab\\tcharacter"', 'tab\tcharacter'], // escaped tab
|
|
59
|
+
['"backslash\\\\test"', 'backslash\\test'], // escaped backslash
|
|
60
|
+
['"unicode \\u263A"', 'unicode ☺'], // unicode escape sequence
|
|
61
|
+
['"surrogate pair: \\uD834\\uDD1E"', 'surrogate pair: 𝄞'], // surrogate pair unicode
|
|
62
|
+
['"emoji: 😀"', 'emoji: 😀'], // literal emoji character
|
|
63
|
+
['"中文"', '中文'], // Chinese characters
|
|
64
|
+
['"Русский"', 'Русский'], // Cyrillic characters
|
|
65
|
+
['"العربية"', 'العربية'], // Arabic characters
|
|
66
|
+
['"𠜎"', '𠜎'], // supplementary plane CJK character
|
|
67
|
+
|
|
68
|
+
// === Numbers ===
|
|
69
|
+
['1234', 1234], // integer
|
|
70
|
+
['1234.5678', 1234.5678], // float
|
|
71
|
+
['0', 0], // zero
|
|
72
|
+
['-0', -0], // negative zero
|
|
73
|
+
['-123', -123], // negative integer
|
|
74
|
+
['-123.456', -123.456], // negative float
|
|
75
|
+
['1e10', 1e10], // positive exponent
|
|
76
|
+
['-1e10', -1e10], // negative exponent
|
|
77
|
+
['1E-10', 1e-10], // negative exponent with uppercase E
|
|
78
|
+
['123.456e7', 123.456e7], // Decimal and exponent
|
|
79
|
+
['123.456E7', 123.456E7], // Decimal and exponent
|
|
80
|
+
['0.0', 0.0], // zero float
|
|
81
|
+
['1.0e+2', 1.0e2], // positive exponent with plus sign
|
|
82
|
+
['1.2345678901234567', 1.2345678901234567], // double precision limit
|
|
83
|
+
['18446744073709551616', 18446744073709552000], // Number larger than MAX_SAFE_INTEGER - might lose precision
|
|
84
|
+
['-18446744073709551616', -18446744073709552000], // Negative number larger than MAX_SAFE_INTEGER
|
|
85
|
+
|
|
86
|
+
// === Booleans and null ===
|
|
87
|
+
['true', true], // boolean true
|
|
88
|
+
['false', false], // boolean false
|
|
89
|
+
['null', null], // null literal
|
|
90
|
+
|
|
91
|
+
// === Empty structures ===
|
|
92
|
+
['{}', {}], // empty object
|
|
93
|
+
['[]', []], // empty array
|
|
94
|
+
['[[]]', [[]]], // nested empty array
|
|
95
|
+
[' [ [ ] ] ', [[]]], // nested empty array with whitespace
|
|
96
|
+
[' [ { } ] ', [{}]], // array with empty object, whitespace
|
|
97
|
+
|
|
98
|
+
// === Arrays ===
|
|
99
|
+
['[1]', [1]], // single element array
|
|
100
|
+
['[1,2]', [1, 2]], // multiple elements
|
|
101
|
+
['[1,[2,3]]', [1, [2, 3]]], // nested arrays
|
|
102
|
+
['[true,false,null]', [true, false, null]], // mixed literals
|
|
103
|
+
['[ "a", "b", "c" ]', ['a', 'b', 'c']], // string array with spaces
|
|
104
|
+
[' [1 , 2 , 3 ] ', [1, 2, 3]], // number array with whitespace
|
|
105
|
+
['[{"a":1},{"b":2}]', [{ a: 1 }, { b: 2 }]], // array of objects
|
|
106
|
+
|
|
107
|
+
// === Objects ===
|
|
108
|
+
['{"a":1}', { a: 1 }], // simple object
|
|
109
|
+
['{"abc":1,"def":2}', { abc: 1, def: 2 }], // multiple key-value pairs
|
|
110
|
+
['{"":true,"a":6.123}', { '': true, a: 6.123 }], // empty string key and mixed values
|
|
111
|
+
['{"nested":{"x":1,"y":[2,3]}}', { nested: { x: 1, y: [2, 3] } }], // nested object and array
|
|
112
|
+
['{ "a" : 1 , "b" : 2 }', { a: 1, b: 2 }], // object with whitespace
|
|
113
|
+
['{"a":{"b":{"c":{"d":{}}}}}', { a: { b: { c: { d: {} } } } }], // deeply nested objects
|
|
114
|
+
['{"a": null, "b": true, "c": false}', { a: null, b: true, c: false }], // mixed value types
|
|
115
|
+
['{"arr":[1,2,3],"obj":{"x":10}}', { arr: [1, 2, 3], obj: { x: 10 } }], // nested array and object
|
|
116
|
+
['{"a":1,"a":2}', { a: 2 }], // duplicate keys, last one wins (per JSON spec)
|
|
117
|
+
|
|
118
|
+
// === Whitespace variations ===
|
|
119
|
+
[' \n\t 1234 ', 1234], // number with leading/trailing whitespace
|
|
120
|
+
[' \n\t "abc" \r\n ', 'abc'], // string with whitespace
|
|
121
|
+
['\n\t{\n\t"a"\t:\n1\n}\n', { a: 1 }], // object with whitespace around tokens
|
|
122
|
+
[' [ \n 1 , \n 2 , \n 3 \n ] ', [1, 2, 3]], // array with whitespace
|
|
123
|
+
[' { "a" : [ 1 , 2 , 3 ] } ', { a: [1, 2, 3] }], // nested with whitespace
|
|
124
|
+
|
|
125
|
+
// === Edge cases and special characters ===
|
|
126
|
+
['"\\u0000"', '\u0000'], // null character
|
|
127
|
+
['"\\b\\f\\n\\r\\t"', '\b\f\n\r\t'], // control characters escapes
|
|
128
|
+
['"\\/"', '/'], // escaped slash
|
|
129
|
+
['"\\\\"', '\\'], // escaped backslash
|
|
130
|
+
['"\\u2028"', '\u2028'], // line separator
|
|
131
|
+
['"\\u2029"', '\u2029'], // paragraph separator
|
|
132
|
+
['"multi\\nline"', 'multi\nline'], // escaped newline in string
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
const invalidInputs: string[] = [
|
|
136
|
+
'"multi\nline"', // literal newline in string (invalid JSON, should fail)
|
|
137
|
+
|
|
138
|
+
'{unquoted_key:1}', // unquoted key
|
|
139
|
+
"{'a':1}", // single quotes
|
|
140
|
+
'1,2,3 trailing]', // garbage after array
|
|
141
|
+
'{ "a": undefined }', // undefined literal
|
|
142
|
+
'{ "a": NaN }', // NaN literal
|
|
143
|
+
'{ "a": Infinity }', // Infinity literal
|
|
144
|
+
'1,2,,3]', // double comma
|
|
145
|
+
'1,2,3,]', // trailing comma
|
|
146
|
+
'{"a":1,}', // trailing comma in object
|
|
147
|
+
'{,}', // comma without key-value
|
|
148
|
+
'[}', // mismatched brackets
|
|
149
|
+
'{"a":}', // missing value
|
|
150
|
+
'{"a":1 "b":2}', // missing comma between pairs
|
|
151
|
+
'{"a":1, "b":2,}', // trailing comma
|
|
152
|
+
'{"a":1,, "b":2}', // double comma
|
|
153
|
+
'{"a":1, "b":2 "c":3}', // missing comma
|
|
154
|
+
'{"a":1 "b":2}', // missing comma
|
|
155
|
+
|
|
156
|
+
'[1,2,3,]', // trailing comma (non-standard, should fail in strict JSON parsers)
|
|
157
|
+
'[1,,2]', // sparse array (non-standard, should fail in strict JSON parsers)
|
|
158
|
+
'{"a":1,}', // trailing comma (non-standard, should fail in strict JSON parsers)
|
|
159
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JsonReviverFunction, parse, parseJSON } from '../exports/Exports.js'
|
|
2
|
+
import { testJson5Encoder } from './Json5EncoderTest.js'
|
|
3
|
+
import { testJson5Parser, testJson5ParserInvalidInputs } from './Json5ParserTests.js'
|
|
4
|
+
import { testJsonEncoder } from './JsonEncoderTests.js'
|
|
5
|
+
import { testJsonParser, testJsonParserInvalidInputs } from './JsonParserTests.js'
|
|
6
|
+
|
|
7
|
+
//testJsonEncoder()
|
|
8
|
+
testJsonParser()
|
|
9
|
+
//testJsonParserInvalidInputs()
|
|
10
|
+
|
|
11
|
+
//testJson5Encoder()
|
|
12
|
+
testJson5Parser()
|
|
13
|
+
//testJson5ParserInvalidInputs()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type JsonReplacerFunction = (key: string, value: any) => any
|
|
2
|
+
export type JsonReplacerArray = (string | number)[]
|
|
3
|
+
export type JsonReplacerType = JsonReplacerFunction | JsonReplacerArray | null
|
|
4
|
+
|
|
5
|
+
export type JsonReviverFunction = (key: string, value: any, context?: string) => any
|
|
6
|
+
|
|
7
|
+
export interface Json5Options {
|
|
8
|
+
enableExtensions: boolean
|
|
9
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
const whitespaceCharactersCharCodesSet = new Set([
|
|
3
|
+
// White space characters:
|
|
4
|
+
0x0009, // Horizontal tab
|
|
5
|
+
0x000A, // Line feed
|
|
6
|
+
0x000B, // Vertical tab
|
|
7
|
+
0x000C, // Form feed
|
|
8
|
+
0x000D, // Carriage return
|
|
9
|
+
0x0020, // Space
|
|
10
|
+
0x00A0, // Non-breaking space
|
|
11
|
+
0x2028, // Line separator
|
|
12
|
+
0x2029, // Paragraph separator
|
|
13
|
+
0xFEFF, // Byte order mark
|
|
14
|
+
|
|
15
|
+
// Unicode extended "Space Separator" category
|
|
16
|
+
0x0020, // Space (SP) - already included above, but listed again in the second list
|
|
17
|
+
0x00A0, // No-Break Space (NBSP) - already included above, but listed again in the second list
|
|
18
|
+
0x1680, // Ogham Space Mark
|
|
19
|
+
0x2000, // En Quad
|
|
20
|
+
0x2001, // Em Quad
|
|
21
|
+
0x2002, // En Space
|
|
22
|
+
0x2003, // Em Space
|
|
23
|
+
0x2004, // Three-Per-Em Space
|
|
24
|
+
0x2005, // Four-Per-Em Space
|
|
25
|
+
0x2006, // Six-Per-Em Space
|
|
26
|
+
0x2007, // Figure Space
|
|
27
|
+
0x2008, // Punctuation Space
|
|
28
|
+
0x2009, // Thin Space
|
|
29
|
+
0x200A, // Hair Space
|
|
30
|
+
0x202F, // Narrow No-Break Space (NNBSP)
|
|
31
|
+
0x205F, // Medium Mathematical Space (MMSP)
|
|
32
|
+
0x3000, // Ideographic Space
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
// Sorted whitespace character ranges:
|
|
36
|
+
//
|
|
37
|
+
// 0x0009 - 0x000D
|
|
38
|
+
// 0x0020
|
|
39
|
+
// 0x00A0
|
|
40
|
+
//
|
|
41
|
+
// 0x1680
|
|
42
|
+
|
|
43
|
+
// 0x2000 - 0x200A
|
|
44
|
+
// 0x2028
|
|
45
|
+
// 0x2029
|
|
46
|
+
// 0x202F
|
|
47
|
+
// 0x205F
|
|
48
|
+
|
|
49
|
+
// 0x3000
|
|
50
|
+
// 0xFEFF
|
|
51
|
+
|