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
(./core.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. Options are honoured on a growing
allowlist, and anything not yet honoured throws a YAMLException rather
than silently diverging. Today only filename (threaded into a thrown
error’s mark), loadAll’s iterator, json: true, and schema as the
default CORE_SCHEMA are accepted (plus any boolean flag left at the value
we already produce, usually false) — e.g. load(text, { json: false }) or
dump(obj, { sortKeys: true }) throws until that option’s sub-task lands. A
relied-upon option fails loud at the call site instead of producing
silently-wrong output.
Maximise drop-in compatibility without ever compromising the two things
that outrank it: YAML-1.2-spec correctness and core (./core.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 fails loud (throws) rather than being silently
ignored; each option sub-task moves its key from the throw-list to the
honoured allowlist below.
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); dump options are not honoured yet and throw (see the matrix).- 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 ./core.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 ./core.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.