Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@cldmv/jsonv

npm version npm downloads GitHub downloads Last commit npm last update

Contributors Sponsor shinrai

Modern JSON parser extending JSON5 with ES2015–2025 features and year‑pinned APIs.

✨ What's New

Latest: v1.1.1 (September 2026)

  • Nine correctness fixes in the template-literal, tolerant-parsing, and reference-resolution paths — mode: "json" / mode: "json5" now actually enforce their feature sets, tolerant mode reports the syntax errors it collects instead of a misleading reference error, forward-reference chains of any length resolve, and parseToAst() tokens/quasis tile the source with no gaps around template interpolations.
  • View full v1.1.1 Changelog

Recent Releases

  • v1.1.0 (September 2026) — parseToAst() returns comments and tokens with positioned keys and corrected spans; reference-resolution errors now carry a position (Release)
  • v1.0.10 (September 2026) — parse errors expose line, column, and offset (Release)
  • v1.0.9 (September 2026) — README doc link for the Prettier plugin in the Tooling section (Release)
  • v1.0.8 (September 2026) — README badge row, contributors/sponsor line, and license badges (Release)

What it is

@cldmv/jsonv is a static data format: JSON5 plus modern literals, with features gated by ECMAScript year modules. It adds internal references (file‑scoped, defined‑before‑use) and forbids executable syntax (no functions, classes, computed keys, or shorthand props).

Core features

  • JSON5 superset (comments, trailing commas, single quotes, hex, etc.)
  • Year‑pinned APIs: @cldmv/jsonv/2011, /2015, /2020, /2021 (2022–2025 re‑export 2021)
  • Modern literals: binary/octal, BigInt, numeric separators
  • Internal references and template interpolation (ES2015+), including forward references
  • Diagnostics: diagnose() and info() for year + feature detection
  • Stringify with json/json5/jsonv modes, BigInt strategies, and raw JSON passthrough
  • Dynamic year loading and resolver utilities (loadYear, resolveYear)
  • Zero dependencies, hand‑written parser

Install

npm install @cldmv/jsonv

Quick start

import { parse, stringify } from "@cldmv/jsonv";

const config = parse(`{
  port: 8080,
  host: "localhost",
  url: `http://${host}:${port}`,
  maxConnections: 1_000_000,
  bigValue: 9007199254740992n
}`);

const text = stringify(config);

Year‑pinned API

Pin a year for stable grammar rules:

import { parse } from "@cldmv/jsonv/2021"; // numeric separators + BigInt
import { parse as parse2015 } from "@cldmv/jsonv/2015"; // binary/octal + templates
import { parse as parse2011 } from "@cldmv/jsonv/2011"; // JSON5 base

See docs/feature-matrix.md and docs/versioning-and-exports.md.

Docs

API surface

Main entry: src/index.mts

Parse options (selected)

  • year: 2011–2025 (defaults to latest)
  • mode: jsonv (default) | json5 (exactly JSON5 1.0) | json (exactly RFC 8259 JSON); see Parse modes
  • allowInternalReferences: default true
  • strictBigInt: require n for unsafe integers (default false)
  • strictOctal: require 0o (reject legacy 0755, default false)
  • tolerant: collect every syntax error instead of stopping at the first; parseWithOptions then throws them together as one JsonvAggregateSyntaxError (see Errors)
  • preserveComments: return comments (with positions) from Parser#parse(); see AST for tooling

Parse modes

mode: "json" accepts exactly RFC 8259 JSON and mode: "json5" accepts exactly JSON5 1.0; mode: "jsonv" (the default) enables every jsonv feature of the selected year. A feature outside the mode throws a positioned JsonvSyntaxError with code: "FEATURE_NOT_ALLOWED_IN_MODE" naming the feature and the mode, and an unknown mode value throws a TypeError. parse() takes the options object in place of the reviver:

import { parse } from "@cldmv/jsonv";

parse('{"a": [1, 2]}', { mode: "json" }); // { a: [1, 2] }
parse("{ a: 1, }", { mode: "json" }); // throws: Unquoted keys not allowed in JSON mode at line 1, column 2
parse("{ a: 1, b: a }", { mode: "json5" }); // throws: Internal references not allowed in JSON5 mode at line 1, column 11

The full feature × mode table is in docs/json5-compatibility.md.

Stringify options (selected)

  • mode: jsonv | json5 | json
  • bigint: native | string | object
  • singleQuote, trailingComma, unquotedKeys
  • preserveNumericFormatting

Full types: src/api-types.mts

Errors

Parse failures throw JsonvSyntaxError (extends SyntaxError, name stays "SyntaxError"), with structured position info alongside the message:

import { parse, JsonvSyntaxError } from "@cldmv/jsonv";

try {
	parse("{ a: 1, }");
} catch (err) {
	if (err instanceof JsonvSyntaxError) {
		console.log(err.line, err.column, err.offset); // 1-based line, message-matching column, 0-based offset
	}
}

This applies to every parse entry point (year-pinned APIs included) and every kind of positioned error — lexer-level (unterminated strings, invalid escapes, year-gated feature checks) and parser-level (unexpected tokens, strict-mode violations) alike.

With tolerant: true, the parser recovers at the next property or element boundary after a syntax error and keeps going. If any syntax error was collected, parseWithOptions (year-pinned APIs included) throws a single JsonvAggregateSyntaxError and does not evaluate the document. It is a JsonvSyntaxError whose own line/column/offset/code are the first error's, whose message is the first error's message followed by the total count, and whose errors array holds every error in source order, each a JsonvSyntaxError with its own position and code (the same error a strict parse would throw for it). A lexical error is reported through the same aggregate. Input without syntax errors evaluates exactly as it does without tolerant:

import { parseWithOptions, JsonvAggregateSyntaxError } from "@cldmv/jsonv";

try {
	parseWithOptions("{ a: 1,, b: 2,, c: }", { tolerant: true });
} catch (err) {
	if (err instanceof JsonvAggregateSyntaxError) {
		err.message; // "Expected property key, got COMMA at line 1, column 7 (3 syntax errors in total)"
		err.errors.map((e) => [e.line, e.column, e.code]); // [[1, 7, "PARSE_ERROR"], [1, 14, "PARSE_ERROR"], [1, 19, "PARSE_ERROR"]]
	}
}

parseToAst never throws for collected errors; it returns them in errors.

Internal-reference resolution failures (an unresolved or circular internal reference) throw the sibling JsonvReferenceError (extends ReferenceError, name stays "ReferenceError") instead, with the same structured line/column/offset/code shape, pointing at the offending reference:

import { parseWithOptions, JsonvReferenceError } from "@cldmv/jsonv";

try {
	parseWithOptions("{ a: missing }");
} catch (err) {
	if (err instanceof JsonvReferenceError) {
		console.log(err.line, err.column, err.offset);
	}
}

AST for tooling

parseToAst() returns the positioned AST without evaluating it, for linters, formatters and editors:

import { parseToAst } from "@cldmv/jsonv"; // also exported from "@cldmv/jsonv/parser"

const { program, comments, tokens, errors } = parseToAst("// port\n{ port: 8080 }");
program.body.properties[0].key; // { type: "Identifier", name: "port", loc: { start: { line: 2, column: 2, offset: 10 }, ... } }
comments[0].value; // " port"

Every node, token and comment carries loc: { start, end } with { line, column, offset } positions (\n, \r\n, \r, U+2028 and U+2029 each count as one line break). Property keys are positioned Literal / Identifier nodes, and Property.loc spans key through value. parseToAst() never throws for invalid input: lexical and parse errors are both collected in errors (with code, line, column and offset), and tolerant: true recovers from both and reports every one. See docs/ast.md for the node reference.

Internal references

{ port: 8080, backup: port, url: `http://${host}:${port}` }

Rules: file‑scoped only, forward references supported, no circular refs.

Year utilities

import { loadYear, getLoadedYear } from "@cldmv/jsonv/loader";
import { resolveYear, isPublishedYear, getPublishedYears } from "@cldmv/jsonv/year-resolver";

const jsonv2023 = await loadYear(2023); // resolves to 2021
const resolved = getLoadedYear(2017); // 2015
const published = getPublishedYears(); // [2011, 2015, 2020, 2021]
const isPublished = isPublishedYear(2021); // true
const nearest = resolveYear(2024); // 2021

Diagnostics

diagnose() returns detected year/features + compatibility flags (json, json5). info() returns only detected year + parsed value.

Tests & fixtures

Tooling

  • ESLint plugin: published separately as @cldmv/eslint-plugin-jsonv (this repo's lint config consumes the published package). For local co-development, clone that repo under the gitignored plugins/eslint-plugin-jsonv/ path and run npm run build:plugin to link it against this repo's current build.
  • Prettier plugin: published separately as @cldmv/prettier-plugin-jsonv for formatting .jsonv files.
  • VS Code language support: published separately as jsonv-vscode; clone under the gitignored plugins/vscode-jsonv/ for local co-development.

Development

npm run dev       # uses src/ via json-dev condition
npm run build     # clean → ts → types → years → cjs → plugin
npm test          # Vitest
npm run lint      # ESLint v9 config

License

GitHub license npm license

Apache-2.0 © Shinrai / CLDMV

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages