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 CHANGED
@@ -1,5 +1,5 @@
1
1
  # smol-toml
2
- [![TOML 1.0.0](https://img.shields.io/badge/TOML-1.0.0-9c4221?style=flat-square)](https://toml.io/en/v1.0.0)
2
+ [![TOML 1.1.0](https://img.shields.io/badge/TOML-1.1.0-9c4221?style=flat-square)](https://toml.io/en/v1.1.0)
3
3
  [![License](https://img.shields.io/github/license/squirrelchat/smol-toml.svg?style=flat-square)](https://github.com/squirrelchat/smol-toml/blob/mistress/LICENSE)
4
4
  [![npm](https://img.shields.io/npm/v/smol-toml?style=flat-square)](https://npm.im/smol-toml)
5
5
  [![Build](https://img.shields.io/github/actions/workflow/status/squirrelchat/smol-toml/build.yaml?style=flat-square&logo=github)](https://github.com/squirrelchat/smol-toml/actions/workflows/build.yaml)
@@ -8,7 +8,7 @@
8
8
  [![Weekly downloads](https://img.shields.io/npm/dw/smol-toml?style=flat-square)](https://npm.im/smol-toml)
9
9
  [![Monthly downloads](https://img.shields.io/npm/dm/smol-toml?style=flat-square)](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.0.0.
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 a bit old. They will be updated in the (hopefully near) future to better
148
- > reflect numbers of the latest version of smol-toml on the latest version of Node.js.
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}:\d{2}(?:\.\d+)?)?(Z|[-+]\d{2}:\d{2})?$/i;
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 { indexOfNewline, skipVoid, skipUntil, skipComment, getStringEnd } from './util.js';
30
+ import { skipVoid, skipUntil, skipComment, getStringEnd } from './util.js';
31
31
  import { TomlError } from './error.js';
32
- function sliceAndTrimEndOf(str, startPtr, endPtr, allowNewLines) {
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
- let trimmed = value.trimEnd();
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
- let newPtr = end ? skipUntil(str, endPtr, ',', end) : endPtr;
66
- if (endPtr - newPtr && end === '}') {
67
- let nextNewLine = indexOfNewline(str, endPtr, newPtr);
68
- if (nextNewLine > -1) {
69
- throw new TomlError('newlines are not allowed in inline tables', {
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: nextNewLine
62
+ ptr: endPtr,
72
63
  });
73
64
  }
74
65
  }
75
- return [value, newPtr];
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, end !== ']');
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] === ',')), end === ']');
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}:\d{2}(?:\.\d+)?)?(Z|[-+]\d{2}:\d{2})?$/i;
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]{4,8}$/i;
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, allowNewLines) {
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
- let trimmed = value.trimEnd();
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
- let newPtr = end ? skipUntil(str, endPtr2, ",", end) : endPtr2;
406
- if (endPtr2 - newPtr && end === "}") {
407
- let nextNewLine = indexOfNewline(str, endPtr2, newPtr);
408
- if (nextNewLine > -1) {
409
- throw new TomlError("newlines are not allowed in inline tables", {
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: nextNewLine
403
+ ptr: endPtr2
412
404
  });
413
405
  }
414
406
  }
415
- return [value, newPtr];
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, end !== "]");
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] === ","), end === "]");
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
- let err = { toml: str, ptr: ptr - 1 };
528
- if (c === "\n") {
529
- throw new TomlError("newlines are not allowed in inline tables", err);
530
- } else if (c === "#") {
531
- throw new TomlError("inline tables cannot contain comments", err);
532
- } else if (c === ",") {
533
- throw new TomlError("expected key-value, found comma", err);
534
- } else if (c !== " " && c !== " ") {
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]{4,8}$/i;
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
- let err = { toml: str, ptr: ptr - 1 };
113
- if (c === '\n') {
114
- throw new TomlError('newlines are not allowed in inline tables', err);
115
- }
116
- else if (c === '#') {
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 !== ' ' && c !== '\t') {
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.5.2",
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.0.3",
22
+ "@tsconfig/node-lts": "^24.0.0",
23
23
  "@tsconfig/strictest": "^2.0.8",
24
- "@types/node": "^24.10.1",
25
- "@vitest/ui": "^4.0.8",
26
- "esbuild": "^0.27.0",
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.8"
30
+ "vitest": "^4.0.16"
31
31
  },
32
32
  "main": "./dist/index.cjs",
33
33
  "module": "./dist/index.js",