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.
Compatibility level TODAY
Section titled “Compatibility level TODAY”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.
Option support matrix
Section titled “Option support matrix”path — done: 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, soload/loadAllreturn 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’scatch (e) { if (e instanceof YAMLException) ... }gets the same “this document is broken” signal js-yaml gives.load/loadAllalso re-throw ourNotImplementedErrorunwrapped rather than mislabeling it aYAMLException— but that path is defensive: the current parser is complete and never throws it. dumpdelegates to ourstringify(implemented); options beyond the value itself are currently ignored.- Custom schemas/tags (
defineScalarTag/defineSequenceTag/defineMappingTag,Schema, the*_SCHEMAconstants, and theschemaoption) 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.