yaml 把显式字符串键 << 仍当成合并指令
yaml 2.9.1 开启 merge 后,!!str << 仍可能合并对象或抛 Merge sources must be maps or map aliases。字面键需核对节点样式与解析后的结构。
YAML 中明明写了 !!str <<: 5,npm 的 yaml 库仍抛出 Merge sources must be maps or map aliases;把值改成对象后,它又把对象里的键合并到了外层。这时需要检查是否开启了 merge,而不是只检查值是不是合法字符串。
yaml 2.9.1 在 merge: true 下有一条回退识别:只要键是 plain 样式的 Scalar、值为 <<,便可能被当成合并键。显式的 !!str 或非特定标签 ! 没有在这条路径中排除它。
字面 << 可以改为带引号的键
如果业务确实需要一个名为 << 的普通键,输入文本可以写成:
"<<":
x: 1
读取时仍保留现有的 merge 选项:
import { parseDocument } from 'yaml';
const doc = parseDocument('"<<": { x: 1 }', { merge: true });
if (doc.errors.length > 0) throw doc.errors[0];
const value = doc.toJS();
value 应保留外层的 << 键,而不是只留下 x。这里修改的是键的语法样式,不需要删除值中的真实数据。若文件由其他工具生成,应在生成入口保持引号,避免下一次保存又还原为 plain 键。
不要对整份 YAML 做字符串替换;注释、字符串正文和真正的合并键都可能包含相同字符。通过 AST 修改时,还要核对 Scalar 的 type 和最终写出的文本,单独设置一个标签并不能避开当前的回退检查。
关闭 merge 会改变整份文档的继承结果
项目如果完全不使用 YAML merge,可以评估在对应解析入口关闭 merge。这个决定会让真正的 <<: *defaults 不再执行原来的合并,不适合只为一个字面键全局关闭。
混合使用配置继承和字面键时,优先改清具体键的表示。不要捕获错误后忽略该字段,也不要为了满足合并器把数字 5 包装成一个无关对象;那只会把原来的数据契约改掉。
正式 2.9.1 的 isMergeKey 仍未检查显式 tag。后续升级需要核对正式包的对应逻辑和回归结果,不能把“正在准备补丁”的状态写成已经修复。
验收至少覆盖带引号的字面键、真正的别名合并,以及相同文件中二者并存的情形。除了确认没有异常,还应比较生成对象的键和值、默认配置是否继续继承,避免从显式报错变成静默丢失字段。


