smol-toml
Scannednpx machina-cli add skill nklisch/skilltap/smol-toml --openclawsmol-toml — TOML Parser Reference
Small, zero-dependency, TOML v1.0.0 spec-compliant parser and serializer. Works in Bun, Node, Deno, and browsers (ESM only).
Install: bun add smol-toml
Import: import { parse, stringify, TomlError, TomlDate } from "smol-toml"
parse(toml: string): Record<string, TomlPrimitive>
Parses a TOML string into a JavaScript object.
import { parse } from "smol-toml"
const config = parse(`
[defaults]
also = ["claude-code"]
yes = false
scope = ""
[security]
scan = "static"
on_warn = "prompt"
threshold = 5
[[taps]]
name = "home"
url = "https://gitea.example.com/nathan/my-skills-tap"
`)
config.defaults.also // ["claude-code"]
config.security.threshold // 5
config.taps // [{ name: "home", url: "https://..." }]
Type mapping (TOML → JavaScript)
| TOML | JavaScript | Notes |
|---|---|---|
| String | string | All four string types (basic, literal, multiline) |
| Integer | number or bigint | bigint if outside safe integer range |
| Float | number | Includes Infinity, -Infinity, NaN |
| Boolean | boolean | |
| Offset Date-Time | TomlDate | e.g. 1979-05-27T07:32:00Z |
| Local Date-Time | TomlDate | e.g. 1979-05-27T07:32:00 |
| Local Date | TomlDate | e.g. 1979-05-27 |
| Local Time | TomlDate | e.g. 07:32:00 |
| Array | Array | Must be homogeneous per TOML spec |
| Table | Record<string, TomlPrimitive> | Plain object |
| Inline Table | Record<string, TomlPrimitive> | Same as table after parsing |
Array of Tables ([[...]]) | Array<Record<string, TomlPrimitive>> | Array of objects |
TomlPrimitive type
type TomlPrimitive =
| string
| number
| bigint
| boolean
| TomlDate
| TomlPrimitive[]
| { [key: string]: TomlPrimitive }
stringify(obj: Record<string, TomlPrimitive>): string
Serializes a JavaScript object to a TOML string.
import { stringify } from "smol-toml"
const toml = stringify({
defaults: {
also: ["claude-code", "cursor"],
yes: false,
scope: "",
},
security: {
scan: "static",
on_warn: "prompt",
require_scan: false,
agent: "",
threshold: 5,
max_size: 51200,
ollama_model: "",
},
"agent-mode": {
enabled: false,
scope: "project",
},
taps: [],
})
What stringify handles
- Strings → properly escaped basic strings
- Numbers → integers or floats
bigint→ TOML integers- Booleans →
true/false Dateobjects → TOML date-timeTomlDate→ preserves original format (local vs offset)- Arrays of primitives → TOML arrays
[1, 2, 3] - Arrays of objects → array of tables
[[section]] - Nested objects → TOML table sections
[parent.child]
What stringify throws on
undefinedvaluesnullvalues- Functions, symbols
- Mixed arrays (primitives + objects together)
- Circular references
TomlError
Custom error thrown on parse failure. Has line/column info for diagnostics.
import { parse, TomlError } from "smol-toml"
try {
parse(raw)
} catch (e) {
if (e instanceof TomlError) {
console.error(`TOML error at line ${e.line}, col ${e.column}:`)
console.error(e.codeblock) // Visual pointer to error location
// e.message — human-readable description
}
}
Properties:
| Property | Type | Description |
|---|---|---|
line | number | Line number (1-indexed) |
column | number | Column number (1-indexed) |
codeblock | string | Formatted snippet with ^ pointer |
message | string | Error description |
TomlDate
Represents TOML date/time values. Preserves which of the four TOML date types was used.
import { TomlDate } from "smol-toml"
const d = new TomlDate("1979-05-27T07:32:00Z")
d.isDateTime() // true (Offset Date-Time)
d.isLocalDateTime() // false
d.isLocalDate() // false
d.isLocalTime() // false
d.toDate() // JavaScript Date object
d.toString() // "1979-05-27T07:32:00Z"
// Local types preserve their format through stringify round-trips
const lt = new TomlDate("07:32:00")
lt.isLocalTime() // true
lt.toString() // "07:32:00"
Pattern: skilltap Config Read/Write
import { parse, stringify, TomlError } from "smol-toml"
import { readFile, writeFile } from "node:fs/promises"
import { ConfigSchema } from "./schemas/config"
import type { Result } from "./types"
export async function loadConfig(path: string): Result<Config> {
let raw: string
try {
raw = await readFile(path, "utf-8")
} catch {
// File doesn't exist — create default
return { ok: true, value: ConfigSchema.parse({}) }
}
let parsed: Record<string, unknown>
try {
parsed = parse(raw)
} catch (e) {
if (e instanceof TomlError) {
return {
ok: false,
error: new UserError(
`Invalid config.toml at line ${e.line}: ${e.message}`
),
}
}
throw e
}
const result = ConfigSchema.safeParse(parsed)
if (!result.success) {
return {
ok: false,
error: new UserError(`Config validation failed: ${result.error.message}`),
}
}
return { ok: true, value: result.data }
}
export async function saveConfig(path: string, config: Config): Promise<void> {
const toml = stringify(config)
await writeFile(path, toml, "utf-8")
}
Limitations
- No comment preservation: Comments are lost during parse → stringify round-trips
- No format preservation: Original whitespace, key ordering, and inline-vs-standard table choices are not preserved
- No streaming: Entire document must be a string
- ESM only: No CommonJS export
- TOML v1.0.0 only: No TOML 1.1 draft features
- Inline tables: Must be single-line per TOML v1.0 spec (smol-toml enforces this)
- Duplicate keys: Throws
TomlError(per spec)
Source
git clone https://github.com/nklisch/skilltap/blob/main/.agents/skills/smol-toml/SKILL.mdView on GitHub Overview
smol-toml is a tiny, spec-compliant TOML v1.0.0 parser and serializer with zero dependencies. It works in Bun, Node, Deno, and browsers (ESM only) and exposes parse, stringify, TomlError, and TomlDate to handle config.toml data in JavaScript projects.
How This Skill Works
It parses a TOML string into a JavaScript object using a defined TomlPrimitive type mapping, returning a Record<string, TomlPrimitive>. stringify performs the reverse, turning a JavaScript object into a TOML string, including support for TomlDate and arrays of tables. TomlError provides diagnostics on parse failures with line/column and a codeblock pointer.
When to Use It
- Reading a config.toml file at startup to configure an app
- Writing updated configuration back to disk after runtime changes
- Working with repeated sections using arrays of tables ([[taps]])
- Diagnosing parse errors during config loading with line/column info
- Using in Bun, Node, Deno, or browser environments via ES module imports
Quick Start
- Step 1: bun add smol-toml
- Step 2: import { parse, stringify } from 'smol-toml'
- Step 3: Use parse(tomlString) to read or stringify(obj) to write TOML
Best Practices
- Use parse for consuming TOML input and stringify for persisting a JS object to TOML
- Leverage TomlDate to preserve TOML date/time semantics (Offset, Local variants)
- Ensure arrays are homogeneous (per TOML rules) to avoid mixed-type arrays
- Catch parse failures with TomlError and inspect line, column, and codeblock
- Avoid undefined or null values when passing objects to stringify
Example Use Cases
- Parse a config.toml with [defaults] and [[taps]] to access config.defaults.also and config.taps
- Serialize a JS config object to TOML including TomlDate values and nested tables
- Parse date/time values and observe TomlDate types for local vs offset times
- Handle an array of tables (taps) to model repeatable sections in config
- Catch and inspect TomlError to pinpoint syntax issues with line/column and codeblock