smol-toml 1.5.2 → 1.6.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/README.md +7 -4
- package/dist/date.js +1 -1
- package/dist/extract.js +13 -22
- package/dist/index.cjs +25 -41
- package/dist/primitive.js +4 -3
- package/dist/struct.js +8 -18
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# smol-toml
|
|
2
|
-
[](https://toml.io/en/v1.1.0)
|
|
3
3
|
[](https://github.com/squirrelchat/smol-toml/blob/mistress/LICENSE)
|
|
4
4
|
[](https://npm.im/smol-toml)
|
|
5
5
|
[](https://github.com/squirrelchat/smol-toml/actions/workflows/build.yaml)
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://npm.im/smol-toml)
|
|
9
9
|
[](https://npm.im/smol-toml)
|
|
10
10
|
|
|
11
|
-
A small, fast, and correct TOML parser and serializer. smol-toml is fully(ish) spec-compliant with TOML v1.
|
|
11
|
+
A small, fast, and correct TOML parser and serializer. smol-toml is fully(ish) spec-compliant with TOML v1.1.0.
|
|
12
12
|
|
|
13
13
|
Why yet another TOML parser? Well, the ecosystem of TOML parsers in JavaScript is quite underwhelming, most likely due
|
|
14
14
|
to a lack of interest. With most parsers being outdated, unmaintained, non-compliant, or a combination of these, a new
|
|
@@ -144,8 +144,11 @@ const localTime = TomlDate.wrapAsLocalTime(jsDate)
|
|
|
144
144
|
|
|
145
145
|
## Performance
|
|
146
146
|
> [!NOTE]
|
|
147
|
-
> These benchmarks are starting to get
|
|
148
|
-
>
|
|
147
|
+
> These benchmarks are starting to get quite old and were ran prior to the release of TOML 1.1.0 which changed subtle
|
|
148
|
+
> implementation details.
|
|
149
|
+
>
|
|
150
|
+
> They will be updated in the (hopefully near) future to better reflect numbers of the latest version of smol-toml
|
|
151
|
+
> on the latest version of Node.js.
|
|
149
152
|
|
|
150
153
|
A note on these performance numbers: in some highly synthetic tests, other parsers such as `fast-toml` greatly
|
|
151
154
|
outperform other parsers, mostly due to their lack of compliance with the spec. For example, to parse a string,
|
package/dist/date.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
|
-
let DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}
|
|
28
|
+
let DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
29
29
|
export class TomlDate extends Date {
|
|
30
30
|
#hasDate = false;
|
|
31
31
|
#hasTime = false;
|
package/dist/extract.js
CHANGED
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { parseString, parseValue } from './primitive.js';
|
|
29
29
|
import { parseArray, parseInlineTable } from './struct.js';
|
|
30
|
-
import {
|
|
30
|
+
import { skipVoid, skipUntil, skipComment, getStringEnd } from './util.js';
|
|
31
31
|
import { TomlError } from './error.js';
|
|
32
|
-
function sliceAndTrimEndOf(str, startPtr, endPtr
|
|
32
|
+
function sliceAndTrimEndOf(str, startPtr, endPtr) {
|
|
33
33
|
let value = str.slice(startPtr, endPtr);
|
|
34
34
|
let commentIdx = value.indexOf('#');
|
|
35
35
|
if (commentIdx > -1) {
|
|
@@ -38,17 +38,7 @@ function sliceAndTrimEndOf(str, startPtr, endPtr, allowNewLines) {
|
|
|
38
38
|
skipComment(str, commentIdx);
|
|
39
39
|
value = value.slice(0, commentIdx);
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
if (!allowNewLines) {
|
|
43
|
-
let newlineIdx = value.indexOf('\n', trimmed.length);
|
|
44
|
-
if (newlineIdx > -1) {
|
|
45
|
-
throw new TomlError('newlines are not allowed in inline tables', {
|
|
46
|
-
toml: str,
|
|
47
|
-
ptr: startPtr + newlineIdx
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return [trimmed, commentIdx];
|
|
41
|
+
return [value.trimEnd(), commentIdx];
|
|
52
42
|
}
|
|
53
43
|
export function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
54
44
|
if (depth === 0) {
|
|
@@ -62,24 +52,25 @@ export function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
|
62
52
|
let [value, endPtr] = c === '['
|
|
63
53
|
? parseArray(str, ptr, depth, integersAsBigInt)
|
|
64
54
|
: parseInlineTable(str, ptr, depth, integersAsBigInt);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
if (end) {
|
|
56
|
+
endPtr = skipVoid(str, endPtr);
|
|
57
|
+
if (str[endPtr] === ',')
|
|
58
|
+
endPtr++;
|
|
59
|
+
else if (str[endPtr] !== end) {
|
|
60
|
+
throw new TomlError('expected comma or end of structure', {
|
|
70
61
|
toml: str,
|
|
71
|
-
ptr:
|
|
62
|
+
ptr: endPtr,
|
|
72
63
|
});
|
|
73
64
|
}
|
|
74
65
|
}
|
|
75
|
-
return [value,
|
|
66
|
+
return [value, endPtr];
|
|
76
67
|
}
|
|
77
68
|
let endPtr;
|
|
78
69
|
if (c === '"' || c === "'") {
|
|
79
70
|
endPtr = getStringEnd(str, ptr);
|
|
80
71
|
let parsed = parseString(str, ptr, endPtr);
|
|
81
72
|
if (end) {
|
|
82
|
-
endPtr = skipVoid(str, endPtr
|
|
73
|
+
endPtr = skipVoid(str, endPtr);
|
|
83
74
|
if (str[endPtr] && str[endPtr] !== ',' && str[endPtr] !== end && str[endPtr] !== '\n' && str[endPtr] !== '\r') {
|
|
84
75
|
throw new TomlError('unexpected character encountered', {
|
|
85
76
|
toml: str,
|
|
@@ -91,7 +82,7 @@ export function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
|
91
82
|
return [parsed, endPtr];
|
|
92
83
|
}
|
|
93
84
|
endPtr = skipUntil(str, ptr, ',', end);
|
|
94
|
-
let slice = sliceAndTrimEndOf(str, ptr, endPtr - (+(str[endPtr - 1] === ','))
|
|
85
|
+
let slice = sliceAndTrimEndOf(str, ptr, endPtr - (+(str[endPtr - 1] === ',')));
|
|
95
86
|
if (!slice[0]) {
|
|
96
87
|
throw new TomlError('incomplete key-value declaration: no value specified', {
|
|
97
88
|
toml: str,
|
package/dist/index.cjs
CHANGED
|
@@ -143,7 +143,7 @@ function getStringEnd(str, seek) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// dist/date.js
|
|
146
|
-
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}
|
|
146
|
+
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
147
147
|
var TomlDate = class _TomlDate extends Date {
|
|
148
148
|
#hasDate = false;
|
|
149
149
|
#hasTime = false;
|
|
@@ -238,13 +238,14 @@ var TomlDate = class _TomlDate extends Date {
|
|
|
238
238
|
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
239
239
|
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
240
240
|
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
241
|
-
var ESCAPE_REGEX = /^[0-9a-f]{
|
|
241
|
+
var ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
|
|
242
242
|
var ESC_MAP = {
|
|
243
243
|
b: "\b",
|
|
244
244
|
t: " ",
|
|
245
245
|
n: "\n",
|
|
246
246
|
f: "\f",
|
|
247
247
|
r: "\r",
|
|
248
|
+
e: "\x1B",
|
|
248
249
|
'"': '"',
|
|
249
250
|
"\\": "\\"
|
|
250
251
|
};
|
|
@@ -279,8 +280,8 @@ function parseString(str, ptr = 0, endPtr = str.length) {
|
|
|
279
280
|
}
|
|
280
281
|
if (isEscape) {
|
|
281
282
|
isEscape = false;
|
|
282
|
-
if (c === "u" || c === "U") {
|
|
283
|
-
let code = str.slice(ptr, ptr += c === "u" ? 4 : 8);
|
|
283
|
+
if (c === "x" || c === "u" || c === "U") {
|
|
284
|
+
let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
|
|
284
285
|
if (!ESCAPE_REGEX.test(code)) {
|
|
285
286
|
throw new TomlError("invalid unicode escape", {
|
|
286
287
|
toml: str,
|
|
@@ -373,24 +374,14 @@ function parseValue(value, toml, ptr, integersAsBigInt) {
|
|
|
373
374
|
}
|
|
374
375
|
|
|
375
376
|
// dist/extract.js
|
|
376
|
-
function sliceAndTrimEndOf(str, startPtr, endPtr
|
|
377
|
+
function sliceAndTrimEndOf(str, startPtr, endPtr) {
|
|
377
378
|
let value = str.slice(startPtr, endPtr);
|
|
378
379
|
let commentIdx = value.indexOf("#");
|
|
379
380
|
if (commentIdx > -1) {
|
|
380
381
|
skipComment(str, commentIdx);
|
|
381
382
|
value = value.slice(0, commentIdx);
|
|
382
383
|
}
|
|
383
|
-
|
|
384
|
-
if (!allowNewLines) {
|
|
385
|
-
let newlineIdx = value.indexOf("\n", trimmed.length);
|
|
386
|
-
if (newlineIdx > -1) {
|
|
387
|
-
throw new TomlError("newlines are not allowed in inline tables", {
|
|
388
|
-
toml: str,
|
|
389
|
-
ptr: startPtr + newlineIdx
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return [trimmed, commentIdx];
|
|
384
|
+
return [value.trimEnd(), commentIdx];
|
|
394
385
|
}
|
|
395
386
|
function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
396
387
|
if (depth === 0) {
|
|
@@ -402,24 +393,25 @@ function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
|
402
393
|
let c = str[ptr];
|
|
403
394
|
if (c === "[" || c === "{") {
|
|
404
395
|
let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
396
|
+
if (end) {
|
|
397
|
+
endPtr2 = skipVoid(str, endPtr2);
|
|
398
|
+
if (str[endPtr2] === ",")
|
|
399
|
+
endPtr2++;
|
|
400
|
+
else if (str[endPtr2] !== end) {
|
|
401
|
+
throw new TomlError("expected comma or end of structure", {
|
|
410
402
|
toml: str,
|
|
411
|
-
ptr:
|
|
403
|
+
ptr: endPtr2
|
|
412
404
|
});
|
|
413
405
|
}
|
|
414
406
|
}
|
|
415
|
-
return [value,
|
|
407
|
+
return [value, endPtr2];
|
|
416
408
|
}
|
|
417
409
|
let endPtr;
|
|
418
410
|
if (c === '"' || c === "'") {
|
|
419
411
|
endPtr = getStringEnd(str, ptr);
|
|
420
412
|
let parsed = parseString(str, ptr, endPtr);
|
|
421
413
|
if (end) {
|
|
422
|
-
endPtr = skipVoid(str, endPtr
|
|
414
|
+
endPtr = skipVoid(str, endPtr);
|
|
423
415
|
if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") {
|
|
424
416
|
throw new TomlError("unexpected character encountered", {
|
|
425
417
|
toml: str,
|
|
@@ -431,7 +423,7 @@ function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
|
431
423
|
return [parsed, endPtr];
|
|
432
424
|
}
|
|
433
425
|
endPtr = skipUntil(str, ptr, ",", end);
|
|
434
|
-
let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ",")
|
|
426
|
+
let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","));
|
|
435
427
|
if (!slice[0]) {
|
|
436
428
|
throw new TomlError("incomplete key-value declaration: no value specified", {
|
|
437
429
|
toml: str,
|
|
@@ -521,17 +513,16 @@ function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
|
521
513
|
let res = {};
|
|
522
514
|
let seen = /* @__PURE__ */ new Set();
|
|
523
515
|
let c;
|
|
524
|
-
let comma = 0;
|
|
525
516
|
ptr++;
|
|
526
517
|
while ((c = str[ptr++]) !== "}" && c) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
} else if (c === "
|
|
533
|
-
|
|
534
|
-
|
|
518
|
+
if (c === ",") {
|
|
519
|
+
throw new TomlError("expected value, found comma", {
|
|
520
|
+
toml: str,
|
|
521
|
+
ptr: ptr - 1
|
|
522
|
+
});
|
|
523
|
+
} else if (c === "#")
|
|
524
|
+
ptr = skipComment(str, ptr);
|
|
525
|
+
else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
|
|
535
526
|
let k;
|
|
536
527
|
let t = res;
|
|
537
528
|
let hasOwn = false;
|
|
@@ -560,15 +551,8 @@ function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
|
560
551
|
seen.add(value);
|
|
561
552
|
t[k] = value;
|
|
562
553
|
ptr = valueEndPtr;
|
|
563
|
-
comma = str[ptr - 1] === "," ? ptr - 1 : 0;
|
|
564
554
|
}
|
|
565
555
|
}
|
|
566
|
-
if (comma) {
|
|
567
|
-
throw new TomlError("trailing commas are not allowed in inline tables", {
|
|
568
|
-
toml: str,
|
|
569
|
-
ptr: comma
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
556
|
if (!c) {
|
|
573
557
|
throw new TomlError("unfinished table encountered", {
|
|
574
558
|
toml: str,
|
package/dist/primitive.js
CHANGED
|
@@ -31,13 +31,14 @@ import { TomlError } from './error.js';
|
|
|
31
31
|
let INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
32
32
|
let FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
33
33
|
let LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
34
|
-
let ESCAPE_REGEX = /^[0-9a-f]{
|
|
34
|
+
let ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
|
|
35
35
|
let ESC_MAP = {
|
|
36
36
|
b: '\b',
|
|
37
37
|
t: '\t',
|
|
38
38
|
n: '\n',
|
|
39
39
|
f: '\f',
|
|
40
40
|
r: '\r',
|
|
41
|
+
e: '\x1b',
|
|
41
42
|
'"': '"',
|
|
42
43
|
'\\': '\\',
|
|
43
44
|
};
|
|
@@ -73,9 +74,9 @@ export function parseString(str, ptr = 0, endPtr = str.length) {
|
|
|
73
74
|
}
|
|
74
75
|
if (isEscape) {
|
|
75
76
|
isEscape = false;
|
|
76
|
-
if (c === 'u' || c === 'U') {
|
|
77
|
+
if (c === 'x' || c === 'u' || c === 'U') {
|
|
77
78
|
// Unicode escape
|
|
78
|
-
let code = str.slice(ptr, (ptr += (c === 'u' ? 4 : 8)));
|
|
79
|
+
let code = str.slice(ptr, (ptr += (c === 'x' ? 2 : c === 'u' ? 4 : 8)));
|
|
79
80
|
if (!ESCAPE_REGEX.test(code)) {
|
|
80
81
|
throw new TomlError('invalid unicode escape', {
|
|
81
82
|
toml: str,
|
package/dist/struct.js
CHANGED
|
@@ -106,20 +106,17 @@ export function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
|
106
106
|
let res = {};
|
|
107
107
|
let seen = new Set();
|
|
108
108
|
let c;
|
|
109
|
-
let comma = 0;
|
|
110
109
|
ptr++;
|
|
111
110
|
while ((c = str[ptr++]) !== '}' && c) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
throw new TomlError('inline tables cannot contain comments', err);
|
|
118
|
-
}
|
|
119
|
-
else if (c === ',') {
|
|
120
|
-
throw new TomlError('expected key-value, found comma', err);
|
|
111
|
+
if (c === ',') {
|
|
112
|
+
throw new TomlError('expected value, found comma', {
|
|
113
|
+
toml: str,
|
|
114
|
+
ptr: ptr - 1,
|
|
115
|
+
});
|
|
121
116
|
}
|
|
122
|
-
else if (c
|
|
117
|
+
else if (c === '#')
|
|
118
|
+
ptr = skipComment(str, ptr);
|
|
119
|
+
else if (c !== ' ' && c !== '\t' && c !== '\n' && c !== '\r') {
|
|
123
120
|
let k;
|
|
124
121
|
let t = res;
|
|
125
122
|
let hasOwn = false;
|
|
@@ -148,15 +145,8 @@ export function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
|
148
145
|
seen.add(value);
|
|
149
146
|
t[k] = value;
|
|
150
147
|
ptr = valueEndPtr;
|
|
151
|
-
comma = str[ptr - 1] === ',' ? ptr - 1 : 0;
|
|
152
148
|
}
|
|
153
149
|
}
|
|
154
|
-
if (comma) {
|
|
155
|
-
throw new TomlError('trailing commas are not allowed in inline tables', {
|
|
156
|
-
toml: str,
|
|
157
|
-
ptr: comma,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
150
|
if (!c) {
|
|
161
151
|
throw new TomlError('unfinished table encountered', {
|
|
162
152
|
toml: str,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smol-toml",
|
|
3
3
|
"license": "BSD-3-Clause",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.0",
|
|
5
5
|
"description": "A small, fast, and correct TOML parser/serializer",
|
|
6
6
|
"author": "Cynthia <cyyynthia@borkenware.com>",
|
|
7
7
|
"repository": "github:squirrelchat/smol-toml",
|
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@iarna/toml": "3.0.0",
|
|
21
21
|
"@ltd/j-toml": "^1.38.0",
|
|
22
|
-
"@tsconfig/node-lts": "^
|
|
22
|
+
"@tsconfig/node-lts": "^24.0.0",
|
|
23
23
|
"@tsconfig/strictest": "^2.0.8",
|
|
24
|
-
"@types/node": "^
|
|
25
|
-
"@vitest/ui": "^4.0.
|
|
26
|
-
"esbuild": "^0.27.
|
|
24
|
+
"@types/node": "^25.0.3",
|
|
25
|
+
"@vitest/ui": "^4.0.16",
|
|
26
|
+
"esbuild": "^0.27.2",
|
|
27
27
|
"fast-toml": "^0.5.4",
|
|
28
28
|
"pin-github-action": "^3.4.0",
|
|
29
29
|
"typescript": "^5.9.3",
|
|
30
|
-
"vitest": "^4.0.
|
|
30
|
+
"vitest": "^4.0.16"
|
|
31
31
|
},
|
|
32
32
|
"main": "./dist/index.cjs",
|
|
33
33
|
"module": "./dist/index.js",
|