Skip to content

js-yaml-compat

js-yaml-compat.ts — a drop-in-ish replacement for the js-yaml v5 public API (load/loadAll/dump), backed by lightning-yaml’s own parser (./index.ts).

This module doc block is the MASTER SOURCE for js-yaml compatibility: it is published verbatim to the website’s API reference (site/astro.config.mjs wires this file through starlight-typedoc), so keep it accurate and up to date.

API-level, not behaviour-complete. Every export and call signature the real js-yaml exposes exists here, so code that imports load/loadAll/ dump compiles and runs unchanged. What is NOT yet honoured is almost every option argument: load(text, { schema, json, maxAliases, maxDepth }) and dump(obj, { sortKeys, indent, noRefs, ... }) are accepted so call sites type-check, but are currently ignored — only filename (threaded into a thrown error’s mark) and loadAll’s iterator actually do anything. The shim is genuinely useful for migrating today, but a call that relies on an option will silently behave differently from real js-yaml.

Maximise drop-in compatibility without ever compromising the two things that outrank it: YAML-1.2-spec correctness and core (./index.ts) speed. Per-option cost is therefore paid either in this shim (pre-/post-processing the plain-JS value, the way the yaml shim’s reviver already does) or behind a gated core seam that leaves the options-free fast path byte-identical. An option we can’t yet honour should eventually FAIL LOUD, not be silently ignored. We are not there yet — this file tracks the gap.

pathdone: already honoured · compat: addable in THIS shim, no core change and no core perf cost · core: gated core change, options-free fast path stays byte-identical · feature: needs a parser/dumper capability that does not exist yet.

load / loadAll (LoadOptions)
filename attach source path to error marks done
json dup-key: last-wins (true) vs throw (false) core [1]
schema FAILSAFE / JSON / CORE / YAML11 typing core [2]
maxAliases cap alias expansions (billion-laughs) compat/core
maxDepth cap nesting depth compat/core (core already tracks depth)
maxTotalMergeKeys cap `<<` merge expansion feature (merge keys unimplemented)
dump (DumpOptions)
sortKeys sort map keys on output compat <- easy win (pre-sort the graph)
skipInvalid drop functions/undefined vs emit/throw compat <- easy win (pre-clean input)
indent block indent width (we hardcode 2) core
quoteStyle prefer 'single' vs "double" core
forceQuotes always quote strings core
schema output schema core
noRefs expand shared refs instead of &/* feature [3]
lineWidth fold long lines feature (no line folding exists)
flowLevel + seqNoIndent + seqInlineFirst + flowBracketPadding feature (no flow-collection writer)
+ flowSkipCommaSpace + flowSkipColonSpace + quoteFlowKeys
+ tagBeforeAnchor
transform mutate documents before dump feature (needs a Document/AST model)

[1] Our default is already last-wins (= json: true). Worth knowing: the yaml-test-suite treats duplicate keys as VALID (case 2JQS), so throw-on-duplicate is a js-yaml-PARITY knob, NOT a spec-conformance win. [2] js-yaml v5’s own default schema is 1.2-core, same as ours — so default typing already agrees; only an explicitly non-default schema diverges. [3] noRefs can’t just skip anchoring: the shared-reference pre-scan is also the cycle guard, so it must first tell a shared DAG node from a cycle.

The construct-level gaps below are the current intentional simplifications (a NotImplementedError here means “can’t read this yet”, not “malformed”):

  • Parser coverage: the core is feature-complete for YAML 1.2 core — block scalars, anchors/aliases, and tags (incl. !!binary) all parse, so load/loadAll return values for them rather than rejecting. The one known gap is merge keys (<<): they are neither merged nor rejected — << comes back as an ordinary key (e.g. { "<<": {...}, y: 2 }), which diverges from js-yaml’s merge semantics. (See the matrix above.)
  • Errors: a genuine syntax error surfaces as a YAMLException (see below), so a caller’s catch (e) { if (e instanceof YAMLException) ... } gets the same “this document is broken” signal js-yaml gives. load/loadAll also re-throw our NotImplementedError unwrapped rather than mislabeling it a YAMLException — but that path is defensive: the current parser is complete and never throws it.
  • dump delegates to our stringify (implemented); options beyond the value itself are currently ignored.
  • Custom schemas/tags (defineScalarTag/defineSequenceTag/ defineMappingTag, Schema, the *_SCHEMA constants, and the schema option) are cheap stubs: they exist so imports resolve and { schema: CORE_SCHEMA }-style options don’t crash the call, but our parser is hardwired to YAML 1.2 core (see ./index.ts) and never branches on them.

One thing that IS aligned rather than merely stubbed: js-yaml’s load throws on a second document in the stream (use loadAll instead), and our parse throws on a second document too (see ./index.ts) — so that particular divergence risk doesn’t exist here.

v5 REWRITE NOTE: js-yaml v5 is a from-scratch rewrite (event-based AST, dual ESM/CJS build with NO default export “by design” per its migration guide). Its Type class and DEFAULT_SCHEMA are gone, replaced by defineScalarTag/defineSequenceTag/defineMappingTag factory functions and a Schema.withTags(...) composition method (.extend() is gone too); this shim’s stubs below have been renamed/reshaped to match. This module still keeps its OWN default export for convenience (not present in real js-yaml v5) since nothing here depends on the real package’s shape for that.