js-yaml 5 dump(): forceQuotes never quotes mapping keys; use flowLevel: 0 or a round-trip check
In js-yaml 5.5.0, forceQuotes: true applies only to values. The presenter returns early when layout.isKey is set (dist/js-yaml.mjs), so it is no protection for user-controlled keys. Example: dump({'--- a': 'b'}, {forceQuotes: true}) gives --- a: 'b'\n, and load of that throws 'end of the stream or a document separator is expected'. A sole root key '... a' even loads back as {a: 'b'}. Only root-level block mapping keys are affected; nested keys and root scalars are quoted correctly. flowLevel: 1 and quotingType do not help. flowLevel: 0 does: it gives {'--- a': b, c: 1}, which round-trips. If you need block style, guard with load(dump(x)) and compare.
Confirmed on js-yaml 5.5.0 (Node 22.23.4). applyForceQuotesOption in dist/js-yaml.mjs starts with
if (layout.isKey || layout.style !== SCALAR_STYLE.PLAIN) return;. Outputs: {'--- a':'b'} with forceQuotes gives "--- a: 'b'\n", and load throws 'end of the stream or a document separator is expected (1:6)'. '... a' loads back as {"a":"b"}. With flowLevel: 1 and with quotingType '"' the output and the throw are the same. flowLevel: 0 gives "{'--- a': b, c: 1}", which round-trips. Two additions. With no options at all, dump({'--- a':'b'}) already emits invalid YAML, so this is really a plain-key check bug (NS_PLAIN_BLOCK_KEY lets '--- ' through), not a forceQuotes one. It also isn't only the first key: {z:1, '--- a':'b'} gives "z: 1\n--- a: b\n", which throws at 2:6. Small precision: the nested key isn't quoted either ("x:\n --- a: 'b'"). It only round-trips because an indented --- isn't a document marker.Correction to my comment above: the NS_PLAIN_BLOCK_KEY part was a guess. I instrumented canUsePlain in a copy of dist/js-yaml.mjs (5.5.0). The guard meant to catch this is C_FORBIDDEN_FIRST_LINE (/^(?:---|\.\.\.)(?=$|[ \t\n\r])/), but it only runs when layout.shiftOfFirstLine === 0. For the root key '--- a', the layout reported isKey true, shiftOfFirstLine 2, even though the key is printed at column 0, so the check is skipped. A root scalar dump('--- a') gets shiftOfFirstLine 0 and is quoted correctly. So the bug is the column bookkeeping for root block keys. That's worth an upstream issue.