Socket
Socket
Sign inDemoInstall

smol-toml

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

smol-toml - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

dist/extract.d.ts

2

dist/date.js

@@ -52,2 +52,4 @@ /*!

date = date.toUpperCase();
if (!offset)
date += 'Z';
}

@@ -54,0 +56,0 @@ }

5

dist/index.d.ts

@@ -28,4 +28,5 @@ /*!

*/
import { type TomlPrimitive } from './util.js';
export { default as TomlError } from './error.js';
export { default as TomlDate } from './date.js';
export declare function parse(toml: string): Record<string, TomlPrimitive>;
export { parse } from './parse.js';
export { stringify } from './stringify.js';

@@ -28,123 +28,5 @@ /*!

*/
import { parseKey } from './struct.js';
import { extractValue } from './parse.js';
import { skipVoid } from './util.js';
import TomlError from './error.js';
export { default as TomlError } from './error.js';
export { default as TomlDate } from './date.js';
function peekTable(key, table, meta, type) {
let t = table;
let m = meta;
let k;
let hasOwn = false;
let state;
for (let i = 0; i < key.length; i++) {
if (i) {
t = hasOwn ? t[k] : (t[k] = {});
m = (state = m[k]).c;
if (type === 0 /* Type.DOTTED */ && state.t === 1 /* Type.EXPLICIT */) {
return null;
}
if (state.t === 2 /* Type.ARRAY */) {
let l = t.length - 1;
t = t[l];
m = m[l].c;
}
}
k = key[i];
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 /* Type.DOTTED */ && m[k]?.d) {
return null;
}
if (!hasOwn) {
if (k === '__proto__') {
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
}
m[k] = {
t: i < key.length - 1 && type === 2 /* Type.ARRAY */
? 0 /* Type.DOTTED */
: type,
d: false,
i: 0,
c: {},
};
}
}
state = m[k];
if (state.t !== type) {
// Bad key type!
return null;
}
if (type === 2 /* Type.ARRAY */) {
if (!state.d) {
state.d = true;
t[k] = [];
}
t[k].push(t = {});
state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: {} });
}
if (state.d) {
// Redefining a table!
return null;
}
state.d = true;
if (type === 1 /* Type.EXPLICIT */) {
t = hasOwn ? t[k] : (t[k] = {});
}
else if (type === 0 /* Type.DOTTED */ && hasOwn) {
return null;
}
return [k, t, state.c];
}
export function parse(toml) {
let res = {};
let meta = {};
let tbl = res;
let m = meta;
for (let ptr = skipVoid(toml, 0); ptr < toml.length;) {
if (toml[ptr] === '[') {
let isTableArray = toml[++ptr] === '[';
let k = parseKey(toml, ptr += +isTableArray, ']');
if (isTableArray) {
if (toml[k[1] - 1] !== ']') {
throw new TomlError('expected end of table declaration', {
toml: toml,
ptr: k[1] - 1,
});
}
k[1]++;
}
let p = peekTable(k[0], res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);
if (!p) {
throw new TomlError('trying to redefine an already defined table or value', {
toml: toml,
ptr: ptr,
});
}
m = p[2];
tbl = p[1];
ptr = k[1];
}
else {
let k = parseKey(toml, ptr);
let p = peekTable(k[0], tbl, m, 0 /* Type.DOTTED */);
if (!p) {
throw new TomlError('trying to redefine an already defined table or value', {
toml: toml,
ptr: ptr,
});
}
let v = extractValue(toml, k[1]);
p[1][p[0]] = v[0];
ptr = v[1];
}
ptr = skipVoid(toml, ptr, true);
if (toml[ptr] && toml[ptr] !== '\n' && toml[ptr] !== '\r') {
throw new TomlError('each key-value declaration must be followed by an end-of-line', {
toml: toml,
ptr: ptr
});
}
ptr = skipVoid(toml, ptr);
}
return res;
}
export { parse } from './parse.js';
export { stringify } from './stringify.js';

@@ -29,2 +29,2 @@ /*!

import { type TomlPrimitive } from './util.js';
export declare function extractValue(str: string, ptr: number, end?: string): [TomlPrimitive, number];
export declare function parse(toml: string): Record<string, TomlPrimitive>;

@@ -28,66 +28,122 @@ /*!

*/
import { parseString, parseValue } from './primitive.js';
import { parseArray, parseInlineTable } from './struct.js';
import { indexOfNewline, skipVoid, skipUntil, skipComment, getStringEnd } from './util.js';
import { parseKey } from './struct.js';
import { extractValue } from './extract.js';
import { skipVoid } from './util.js';
import TomlError from './error.js';
function sliceAndTrimEndOf(str, startPtr, endPtr, allowNewLines) {
let value = str.slice(startPtr, endPtr);
let commentIdx = value.indexOf('#');
if (commentIdx > -1) {
// The call to skipComment allows to "validate" the comment
// (absence of control characters)
skipComment(str, commentIdx);
value = value.slice(0, commentIdx);
function peekTable(key, table, meta, type) {
let t = table;
let m = meta;
let k;
let hasOwn = false;
let state;
for (let i = 0; i < key.length; i++) {
if (i) {
t = hasOwn ? t[k] : (t[k] = {});
m = (state = m[k]).c;
if (type === 0 /* Type.DOTTED */ && state.t === 1 /* Type.EXPLICIT */) {
return null;
}
if (state.t === 2 /* Type.ARRAY */) {
let l = t.length - 1;
t = t[l];
m = m[l].c;
}
}
k = key[i];
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 /* Type.DOTTED */ && m[k]?.d) {
return null;
}
if (!hasOwn) {
if (k === '__proto__') {
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
}
m[k] = {
t: i < key.length - 1 && type === 2 /* Type.ARRAY */
? 0 /* Type.DOTTED */
: type,
d: false,
i: 0,
c: {},
};
}
}
let trimmed = value.trimEnd();
if (!allowNewLines) {
let newlineIdx = value.indexOf('\n', trimmed.length);
if (newlineIdx > -1) {
throw new TomlError('newlines are not allowed in inline tables', {
toml: str,
ptr: startPtr + newlineIdx
});
state = m[k];
if (state.t !== type) {
// Bad key type!
return null;
}
if (type === 2 /* Type.ARRAY */) {
if (!state.d) {
state.d = true;
t[k] = [];
}
t[k].push(t = {});
state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: {} });
}
return [trimmed, commentIdx];
if (state.d) {
// Redefining a table!
return null;
}
state.d = true;
if (type === 1 /* Type.EXPLICIT */) {
t = hasOwn ? t[k] : (t[k] = {});
}
else if (type === 0 /* Type.DOTTED */ && hasOwn) {
return null;
}
return [k, t, state.c];
}
export function extractValue(str, ptr, end) {
let c = str[ptr];
if (c === '[' || c === '{') {
let [value, endPtr] = c === '['
? parseArray(str, ptr)
: parseInlineTable(str, ptr);
let newPtr = skipUntil(str, endPtr, ',', end);
if (end === '}') {
let nextNewLine = indexOfNewline(str, endPtr, newPtr);
if (nextNewLine > -1) {
throw new TomlError('newlines are not allowed in inline tables', {
toml: str,
ptr: nextNewLine
export function parse(toml) {
let res = {};
let meta = {};
let tbl = res;
let m = meta;
for (let ptr = skipVoid(toml, 0); ptr < toml.length;) {
if (toml[ptr] === '[') {
let isTableArray = toml[++ptr] === '[';
let k = parseKey(toml, ptr += +isTableArray, ']');
if (isTableArray) {
if (toml[k[1] - 1] !== ']') {
throw new TomlError('expected end of table declaration', {
toml: toml,
ptr: k[1] - 1,
});
}
k[1]++;
}
let p = peekTable(k[0], res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);
if (!p) {
throw new TomlError('trying to redefine an already defined table or value', {
toml: toml,
ptr: ptr,
});
}
m = p[2];
tbl = p[1];
ptr = k[1];
}
return [value, newPtr];
else {
let k = parseKey(toml, ptr);
let p = peekTable(k[0], tbl, m, 0 /* Type.DOTTED */);
if (!p) {
throw new TomlError('trying to redefine an already defined table or value', {
toml: toml,
ptr: ptr,
});
}
let v = extractValue(toml, k[1]);
p[1][p[0]] = v[0];
ptr = v[1];
}
ptr = skipVoid(toml, ptr, true);
if (toml[ptr] && toml[ptr] !== '\n' && toml[ptr] !== '\r') {
throw new TomlError('each key-value declaration must be followed by an end-of-line', {
toml: toml,
ptr: ptr
});
}
ptr = skipVoid(toml, ptr);
}
let endPtr;
if (c === '"' || c === "'") {
endPtr = getStringEnd(str, ptr);
return [parseString(str, ptr, endPtr), endPtr + +(!!end && str[endPtr] === ',')];
}
endPtr = skipUntil(str, ptr, ',', end);
let slice = sliceAndTrimEndOf(str, ptr, endPtr - (+(str[endPtr - 1] === ',')), end === ']');
if (!slice[0]) {
throw new TomlError('incomplete key-value declaration: no value specified', {
toml: str,
ptr: ptr
});
}
if (end && slice[1] > -1) {
endPtr = skipVoid(str, ptr + slice[1]);
endPtr += +(str[endPtr] === ',');
}
return [
parseValue(slice[0], str, ptr),
endPtr,
];
return res;
}

@@ -138,6 +138,6 @@ /*!

return NaN;
if (value === '-0')
return 0; // Avoid FP representation of -0
// Numbers
let isInt;
if (value === '-0')
return 0; // Avoid FP representation of -0
if ((isInt = INT_REGEX.test(value)) || FLOAT_REGEX.test(value)) {

@@ -144,0 +144,0 @@ if (LEADING_ZERO.test(value)) {

@@ -29,3 +29,3 @@ /*!

import { parseString } from './primitive.js';
import { extractValue } from './parse.js';
import { extractValue } from './extract.js';
import { skipComment, indexOfNewline, getStringEnd, skipVoid } from './util.js';

@@ -32,0 +32,0 @@ import TomlError from './error.js';

{
"name": "smol-toml",
"version": "1.0.1",
"version": "1.1.0",
"keywords": [
"toml",
"parser"
"parser",
"serializer"
],
"description": "A small, fast, and correct TOML parser",
"description": "A small, fast, and correct TOML parser/serializer",
"repository": "git@github.com:squirrelchat/smol-toml.git",

@@ -10,0 +11,0 @@ "author": "Cynthia <cyyynthia@borkenware.com>",

@@ -6,3 +6,3 @@ # smol-toml

A small, fast, and correct TOML parser. smol-toml is fully(ish) spec-compliant with TOML v1.0.0.
A small, fast, and correct TOML parser and serializer. smol-toml is fully(ish) spec-compliant with TOML v1.0.0.

@@ -27,2 +27,3 @@ Why yet another TOML parser? Well, the ecosystem of TOML parsers in JavaScript is quite underwhelming, most likely due

<summary>List of failed `toml-test` cases</summary>
These tests were done by modifying `primitive.ts` and make the implementation return bigints for integers. This allows

@@ -33,3 +34,3 @@ verifying the parser correctly intents a number to be an integer or a float.

The following tests are failing:
The following parse tests are failing:
- invalid/encoding/bad-utf8-in-comment

@@ -50,3 +51,3 @@ - invalid/encoding/bad-utf8-in-multiline-literal

```js
import { parse } from 'smol-toml'
import { parse, stringify } from 'smol-toml'

@@ -56,4 +57,58 @@ const doc = '...'

console.log(parsed)
const toml = stringify(parsed)
console.log(toml)
```
A few notes on the `stringify` function:
- `undefined` and `null` values on objects are ignored (does not produce a key/value).
- `undefined` and `null` values in arrays are **rejected**.
- Functions, classes and symbols are **rejected**.
- floats will be serialized as integers if they don't have a decimal part.
- `stringify(parse('a = 1.0')) === 'a = 1'`
- JS `Date` will be serialized as Offset Date Time
- Use the [`TomlDate` object](#dates) for representing other types.
### Dates
`smol-toml` uses an extended `Date` object to represent all types of TOML Dates. In the future, `smol-toml` will use
objects from the Temporal proposal, but for now we're stuck with the legacy Date object.
```js
import { TomlDate } from 'smol-toml'
// Offset Date Time
const date = new TomlDate('1979-05-27T07:32:00.000-08:00')
console.log(date.isDateTime(), date.isDate(), date.isTime(), date.isLocal()) // ~> true, false, false, false
console.log(date.toISOString()) // ~> 1979-05-27T07:32:00.000-08:00
// Local Date Time
const date = new TomlDate('1979-05-27T07:32:00.000')
console.log(date.isDateTime(), date.isDate(), date.isTime(), date.isLocal()) // ~> true, false, false, true
console.log(date.toISOString()) // ~> 1979-05-27T07:32:00.000
// Local Date
const date = new TomlDate('1979-05-27')
console.log(date.isDateTime(), date.isDate(), date.isTime(), date.isLocal()) // ~> false, true, false, true
console.log(date.toISOString()) // ~> 1979-05-27
// Local Time
const date = new TomlDate('07:32:00')
console.log(date.isDateTime(), date.isDate(), date.isTime(), date.isLocal()) // ~> false, false, true, true
console.log(date.toISOString()) // ~> 07:32:00.000
```
You can also wrap a native `Date` object and specify using different methods depending on the type of date you wish
to represent:
```js
import { TomlDate } from 'smol-toml'
const jsDate = new Date()
const offsetDateTime = TomlDate.wrapAsOffsetDateTime(jsDate)
const localDateTime = TomlDate.wrapAsLocalDateTime(jsDate)
const localDate = TomlDate.wrapAsLocalDate(jsDate)
const localTime = TomlDate.wrapAsLocalTime(jsDate)
```
## Performance

@@ -69,11 +124,16 @@ A note on these performance numbers: in some highly synthetic tests, other parsers such as `fast-toml` greatly

| | smol-toml | @iarna/toml@3.0.0 | @ltd/j-toml | fast-toml |
|----------------|---------------------|-------------------|----------------|----------------|
| Spec example | **71,356.51 op/s** | 33,629.31 op/s | 16,433.86 op/s | 29,421.60 op/s |
| ~5MB test file | **3.8091 op/s** | *DNF* | 2.4369 op/s | 2.6078 op/s |
| **Parse** | smol-toml | @iarna/toml@3.0.0 | @ltd/j-toml | fast-toml |
|----------------|---------------------|-------------------|-----------------|-----------------|
| Spec example | **71,356.51 op/s** | 33,629.31 op/s | 16,433.86 op/s | 29,421.60 op/s |
| ~5MB test file | **3.8091 op/s** | *DNF* | 2.4369 op/s | 2.6078 op/s |
| **Stringify** | smol-toml | @iarna/toml@3.0.0 | @ltd/j-toml |
|----------------|----------------------|-------------------|----------------|
| Spec example | **195,191.99 op/s** | 46,583.07 op/s | 5,670.12 op/s |
| ~5MB test file | **14.6709 op/s** | 3.5941 op/s | 0.7856 op/s |
<details>
<summary>Detailed benchmark data</summary>
Tests ran using Vitest v0.31.0 on commit 04d233e351f9ae719222154ee2217aea8b95dbab
Tests ran using Vitest v0.31.0 on commit f58cb6152e667e9cea09f31c93d90652e3b82bf5

@@ -96,2 +156,12 @@ CPU: Intel Core i7 7700K (4.2GHz)

· fast-toml 2.6078 373.88 412.79 383.47 388.62 412.79 412.79 412.79 ±2.72% 10
✓ bench/stringifySpecExample.bench.ts (3) 1886ms
name hz min max mean p75 p99 p995 p999 rme samples
· smol-toml 195,191.99 0.0047 0.2704 0.0051 0.0050 0.0099 0.0110 0.0152 ±0.41% 97596 fastest
· @iarna/toml 46,583.07 0.0197 0.2808 0.0215 0.0208 0.0448 0.0470 0.1704 ±0.47% 23292
· @ltd/j-toml 5,670.12 0.1613 0.5768 0.1764 0.1726 0.3036 0.3129 0.4324 ±0.56% 2836 slowest
✓ bench/stringifyLargeMixed.bench.ts (3) 24057ms
name hz min max mean p75 p99 p995 p999 rme samples
· smol-toml 14.6709 65.1071 79.2199 68.1623 67.1088 79.2199 79.2199 79.2199 ±5.25% 10 fastest
· @iarna/toml 3.5941 266.48 295.24 278.24 290.10 295.24 295.24 295.24 ±2.83% 10
· @ltd/j-toml 0.7856 1,254.33 1,322.05 1,272.87 1,286.82 1,322.05 1,322.05 1,322.05 ±1.37% 10 slowest

@@ -109,2 +179,10 @@

4.34x faster than @ltd/j-toml
smol-toml - bench/stringifyLargeMixed.bench.ts >
4.00x faster than @iarna/toml
18.33x faster than @ltd/j-toml
smol-toml - bench/stringifySpecExample.bench.ts >
4.19x faster than @iarna/toml
34.42x faster than @ltd/j-toml
```

@@ -122,5 +200,5 @@

For the reference anyways, `toml-nodejs` (with proper imports) is ~8x slower on both benchmark with:
For the reference anyways, `toml-nodejs` (with proper imports) is ~8x slower on both parse benchmark with:
- spec example: 7,543.47 op/s
- 5mb mixed: 0.7006 op/s
</details>
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc