hjson-js
A human-friendly JSON superset with comments, quoteless strings, and optional commas — parse and stringify config files without sacrificing readability.
Repository Health
Technical Analysis
Hjson is the JavaScript reference implementation of the Hjson syntax — a relaxed superset of JSON designed to be edited by humans rather than only generated by machines. It extends standard JSON parsing to accept # and ////* */ comments, quoteless (bare) strings, optional trailing commas, and multiline triple-quoted strings, while still stringifying back to valid JSON or Hjson on demand.
The library ships both a Node.js/browser API (Hjson.parse / Hjson.stringify) and a standalone hjson CLI for converting files between JSON and Hjson from the command line. A round-trip mode (Hjson.rt) preserves whitespace and comments when a config file is parsed, edited programmatically, and written back out, which is the library’s main draw for tooling that needs to modify a user’s config without clobbering their annotations. Optional domain-specific formats (DSF) add support for values like Inf/NaN and ISO dates that plain JSON can’t represent.
What You Get
- A drop-in
Hjson.parse/Hjson.stringifyAPI that reads/writes both plain JSON and the more permissive Hjson syntax - A
require-confighook (require('hjson/lib/require-config')) that lets Node.jsrequire()a.hjsonfile directly - Round-trip mode (
Hjson.rt) that preserves comments and whitespace when a config is parsed, mutated, and re-serialized - A standalone
hjsoncommand-line tool for converting files between JSON and Hjson, with colorized terminal output - Configurable stringify output — quote styles, indentation, comma separators, condensed single-line objects/arrays, and custom EOL
- Optional domain-specific formats (DSF) for
Inf/NaN/-0math values and ISO date parsing beyond what plain JSON supports
Common Use Cases
- Loading application config files that need to stay human-editable and support inline comments explaining each setting
- Building a CLI or build-tool step that converts a repo’s
.jsonfiles to commented.hjson(or back) for editing - Programmatically updating one field in a user’s existing Hjson config while preserving their comments and formatting
- Accepting a more forgiving input format (missing commas, unquoted strings) from end users or config authors
- Bundling Hjson support into an Electron or browser app via the prebuilt
bundle/hjson.min.jsUMD build
Under The Hood
Architecture
The package is composed of small, single-purpose modules under lib/ — hjson-parse.js, hjson-stringify.js, hjson-comments.js, hjson-common.js, and hjson-dsf.js — wired together by hjson.js, which acts as a thin composition root exposing parse, stringify, rt (round-trip), dsf, and comments. Both parser and stringifier are hand-written, character-by-character state machines (a closure-scoped at/ch cursor with next()/peek()) rather than built on a parser-generator or regex-based tokenizer, which keeps the runtime dependency-free but means the two directions (parse vs. stringify) each reimplement their own traversal logic rather than sharing an intermediate AST. Comment preservation is bolted on separately: parsed values get a non-enumerable __COMMENTS__ property (via hjson-common.createComment) that hjson-comments.js can later extract into a portable comment tree or merge back onto a fresh object, which is what makes the Hjson.rt round-trip mode work without a full CST. There is no dependency-injection or plugin architecture beyond the DSF (domain-specific format) hook, which lets math/hex/date extensions register additional parse/stringify handlers without touching the core scanner.
Tech Stack
The runtime has zero dependencies — pure ES5-era JavaScript ("use strict", function-based modules, no ES6 syntax) that runs unmodified in old Node.js versions and browsers alike (the .travis.yml matrix tests Node 0.10 through 8). A single Node built-in (os.EOL) is used for line-ending defaults and guarded so it degrades gracefully in a browser bundle. devDependencies only cover the build pipeline: browserify/browserify-header to produce the UMD bundle/hjson.js, uglify-js to minify it, and eslint for linting; there is no TypeScript, no bundler for the library itself, and no test runner beyond a hand-rolled script. The CLI (bin/hjson) is a thin Node script with manual argv parsing (no commander/yargs).
Code Quality
Tests live in test/test.js, a ~90-line hand-written runner (not Jest/Mocha/etc.) that reads a testlist.txt fixture list under test/assets/, round-trips each fixture through parse and stringify, and diffs the output against expected _result.json/_result.hjson files across four CRLF/LF permutations — a solid fixture-driven regression suite for a project this size, though it has no coverage reporting and no CI badge currently passing (the linked Travis CI service is defunct). There are no type annotations (plain JS, no JSDoc types, no TypeScript defs shipped), and error handling is done via thrown Error/SyntaxError objects with hand-built line/column context (error() in hjson-parse.js) rather than a structured error type. Naming is consistent and the modules are small and readable, but the character-scanning code relies on shared mutable closure state (at, ch) which is a common pattern for hand-written parsers but harder to unit-test in isolation than a functional/immutable parser combinator style.
What Makes It Unique
Hjson’s distinguishing technical choice is treating comment/whitespace preservation as a first-class, opt-in mode (keepWsc) rather than an afterthought — most JSON-superset parsers (JSON5, JSONC) discard comments on parse, but Hjson’s rt (round-trip) API and separate hjson-comments.js extraction/merge functions let a program parse a human-edited config, mutate specific fields programmatically, and re-serialize it with the original comments and formatting intact. Its quoteless-string grammar (bare text up to end-of-line, tf/nn/null keyword detection, DSF-extensible) is also more permissive than JSON5’s, prioritizing what a non-technical human would naturally type in a config file over strict grammar minimalism.