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.
Files changed (84) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +66 -0
  3. package/dist/exports/Exports.d.ts +6 -0
  4. package/dist/exports/Exports.d.ts.map +1 -0
  5. package/dist/exports/Exports.js +6 -0
  6. package/dist/exports/Exports.js.map +1 -0
  7. package/dist/json/JsonEncoder.d.ts +3 -0
  8. package/dist/json/JsonEncoder.d.ts.map +1 -0
  9. package/dist/json/JsonEncoder.js +147 -0
  10. package/dist/json/JsonEncoder.js.map +1 -0
  11. package/dist/json/JsonParser.d.ts +3 -0
  12. package/dist/json/JsonParser.d.ts.map +1 -0
  13. package/dist/json/JsonParser.js +378 -0
  14. package/dist/json/JsonParser.js.map +1 -0
  15. package/dist/json5/Json5Encoder.d.ts +3 -0
  16. package/dist/json5/Json5Encoder.d.ts.map +1 -0
  17. package/dist/json5/Json5Encoder.js +155 -0
  18. package/dist/json5/Json5Encoder.js.map +1 -0
  19. package/dist/json5/Json5Parser.d.ts +3 -0
  20. package/dist/json5/Json5Parser.d.ts.map +1 -0
  21. package/dist/json5/Json5Parser.js +781 -0
  22. package/dist/json5/Json5Parser.js.map +1 -0
  23. package/dist/tests/Json5EncoderTest.d.ts +2 -0
  24. package/dist/tests/Json5EncoderTest.d.ts.map +1 -0
  25. package/dist/tests/Json5EncoderTest.js +44 -0
  26. package/dist/tests/Json5EncoderTest.js.map +1 -0
  27. package/dist/tests/Json5ParserTests.d.ts +3 -0
  28. package/dist/tests/Json5ParserTests.d.ts.map +1 -0
  29. package/dist/tests/Json5ParserTests.js +211 -0
  30. package/dist/tests/Json5ParserTests.js.map +1 -0
  31. package/dist/tests/JsonEncoderTests.d.ts +2 -0
  32. package/dist/tests/JsonEncoderTests.d.ts.map +1 -0
  33. package/dist/tests/JsonEncoderTests.js +44 -0
  34. package/dist/tests/JsonEncoderTests.js.map +1 -0
  35. package/dist/tests/JsonParserTests.d.ts +3 -0
  36. package/dist/tests/JsonParserTests.d.ts.map +1 -0
  37. package/dist/tests/JsonParserTests.js +140 -0
  38. package/dist/tests/JsonParserTests.js.map +1 -0
  39. package/dist/tests/Test.d.ts +2 -0
  40. package/dist/tests/Test.d.ts.map +1 -0
  41. package/dist/tests/Test.js +9 -0
  42. package/dist/tests/Test.js.map +1 -0
  43. package/dist/types/Types.d.ts +8 -0
  44. package/dist/types/Types.d.ts.map +1 -0
  45. package/dist/types/Types.js +2 -0
  46. package/dist/types/Types.js.map +1 -0
  47. package/dist/unused/Unused.d.ts +2 -0
  48. package/dist/unused/Unused.d.ts.map +1 -0
  49. package/dist/unused/Unused.js +47 -0
  50. package/dist/unused/Unused.js.map +1 -0
  51. package/dist/utilities/JsonParseError.d.ts +7 -0
  52. package/dist/utilities/JsonParseError.d.ts.map +1 -0
  53. package/dist/utilities/JsonParseError.js +10 -0
  54. package/dist/utilities/JsonParseError.js.map +1 -0
  55. package/dist/utilities/ObjectUtilities.d.ts +2 -0
  56. package/dist/utilities/ObjectUtilities.d.ts.map +1 -0
  57. package/dist/utilities/ObjectUtilities.js +142 -0
  58. package/dist/utilities/ObjectUtilities.js.map +1 -0
  59. package/dist/utilities/TypedArray.d.ts +4 -0
  60. package/dist/utilities/TypedArray.d.ts.map +1 -0
  61. package/dist/utilities/TypedArray.js +2 -0
  62. package/dist/utilities/TypedArray.js.map +1 -0
  63. package/dist/utilities/Utilities.d.ts +17 -0
  64. package/dist/utilities/Utilities.d.ts.map +1 -0
  65. package/dist/utilities/Utilities.js +97 -0
  66. package/dist/utilities/Utilities.js.map +1 -0
  67. package/package.json +38 -0
  68. package/src/exports/Exports.ts +7 -0
  69. package/src/json/JsonEncoder.ts +180 -0
  70. package/src/json/JsonParser.ts +491 -0
  71. package/src/json5/Json5Encoder.ts +189 -0
  72. package/src/json5/Json5Parser.ts +959 -0
  73. package/src/tests/Json5EncoderTest.ts +51 -0
  74. package/src/tests/Json5ParserTests.ts +237 -0
  75. package/src/tests/JsonEncoderTests.ts +51 -0
  76. package/src/tests/JsonParserTests.ts +159 -0
  77. package/src/tests/Test.ts +13 -0
  78. package/src/types/Types.ts +9 -0
  79. package/src/unused/Unused.ts +51 -0
  80. package/src/utilities/JsonParseError.ts +7 -0
  81. package/src/utilities/ObjectUtilities.ts +184 -0
  82. package/src/utilities/TypedArray.ts +39 -0
  83. package/src/utilities/Utilities.ts +130 -0
  84. 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
+
@@ -0,0 +1,7 @@
1
+ import { PositionInfo} from "./Utilities.js"
2
+
3
+ export class JsonParserError extends Error {
4
+ constructor(public readonly message: string, public readonly positionInfo: PositionInfo) {
5
+ super(message)
6
+ }
7
+ }