151 lines
5.1 KiB
JavaScript
151 lines
5.1 KiB
JavaScript
|
import { isCollection, isNode, isScalar, isSeq } from '../nodes/identity.js';
|
||
|
import { Scalar } from '../nodes/Scalar.js';
|
||
|
import { stringify } from './stringify.js';
|
||
|
import { lineComment, indentComment } from './stringifyComment.js';
|
||
|
|
||
|
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
|
||
|
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
|
||
|
let keyComment = (isNode(key) && key.comment) || null;
|
||
|
if (simpleKeys) {
|
||
|
if (keyComment) {
|
||
|
throw new Error('With simple keys, key nodes cannot have comments');
|
||
|
}
|
||
|
if (isCollection(key) || (!isNode(key) && typeof key === 'object')) {
|
||
|
const msg = 'With simple keys, collection cannot be used as a key value';
|
||
|
throw new Error(msg);
|
||
|
}
|
||
|
}
|
||
|
let explicitKey = !simpleKeys &&
|
||
|
(!key ||
|
||
|
(keyComment && value == null && !ctx.inFlow) ||
|
||
|
isCollection(key) ||
|
||
|
(isScalar(key)
|
||
|
? key.type === Scalar.BLOCK_FOLDED || key.type === Scalar.BLOCK_LITERAL
|
||
|
: typeof key === 'object'));
|
||
|
ctx = Object.assign({}, ctx, {
|
||
|
allNullValues: false,
|
||
|
implicitKey: !explicitKey && (simpleKeys || !allNullValues),
|
||
|
indent: indent + indentStep
|
||
|
});
|
||
|
let keyCommentDone = false;
|
||
|
let chompKeep = false;
|
||
|
let str = stringify(key, ctx, () => (keyCommentDone = true), () => (chompKeep = true));
|
||
|
if (!explicitKey && !ctx.inFlow && str.length > 1024) {
|
||
|
if (simpleKeys)
|
||
|
throw new Error('With simple keys, single line scalar must not span more than 1024 characters');
|
||
|
explicitKey = true;
|
||
|
}
|
||
|
if (ctx.inFlow) {
|
||
|
if (allNullValues || value == null) {
|
||
|
if (keyCommentDone && onComment)
|
||
|
onComment();
|
||
|
return str === '' ? '?' : explicitKey ? `? ${str}` : str;
|
||
|
}
|
||
|
}
|
||
|
else if ((allNullValues && !simpleKeys) || (value == null && explicitKey)) {
|
||
|
str = `? ${str}`;
|
||
|
if (keyComment && !keyCommentDone) {
|
||
|
str += lineComment(str, ctx.indent, commentString(keyComment));
|
||
|
}
|
||
|
else if (chompKeep && onChompKeep)
|
||
|
onChompKeep();
|
||
|
return str;
|
||
|
}
|
||
|
if (keyCommentDone)
|
||
|
keyComment = null;
|
||
|
if (explicitKey) {
|
||
|
if (keyComment)
|
||
|
str += lineComment(str, ctx.indent, commentString(keyComment));
|
||
|
str = `? ${str}\n${indent}:`;
|
||
|
}
|
||
|
else {
|
||
|
str = `${str}:`;
|
||
|
if (keyComment)
|
||
|
str += lineComment(str, ctx.indent, commentString(keyComment));
|
||
|
}
|
||
|
let vsb, vcb, valueComment;
|
||
|
if (isNode(value)) {
|
||
|
vsb = !!value.spaceBefore;
|
||
|
vcb = value.commentBefore;
|
||
|
valueComment = value.comment;
|
||
|
}
|
||
|
else {
|
||
|
vsb = false;
|
||
|
vcb = null;
|
||
|
valueComment = null;
|
||
|
if (value && typeof value === 'object')
|
||
|
value = doc.createNode(value);
|
||
|
}
|
||
|
ctx.implicitKey = false;
|
||
|
if (!explicitKey && !keyComment && isScalar(value))
|
||
|
ctx.indentAtStart = str.length + 1;
|
||
|
chompKeep = false;
|
||
|
if (!indentSeq &&
|
||
|
indentStep.length >= 2 &&
|
||
|
!ctx.inFlow &&
|
||
|
!explicitKey &&
|
||
|
isSeq(value) &&
|
||
|
!value.flow &&
|
||
|
!value.tag &&
|
||
|
!value.anchor) {
|
||
|
// If indentSeq === false, consider '- ' as part of indentation where possible
|
||
|
ctx.indent = ctx.indent.substring(2);
|
||
|
}
|
||
|
let valueCommentDone = false;
|
||
|
const valueStr = stringify(value, ctx, () => (valueCommentDone = true), () => (chompKeep = true));
|
||
|
let ws = ' ';
|
||
|
if (keyComment || vsb || vcb) {
|
||
|
ws = vsb ? '\n' : '';
|
||
|
if (vcb) {
|
||
|
const cs = commentString(vcb);
|
||
|
ws += `\n${indentComment(cs, ctx.indent)}`;
|
||
|
}
|
||
|
if (valueStr === '' && !ctx.inFlow) {
|
||
|
if (ws === '\n')
|
||
|
ws = '\n\n';
|
||
|
}
|
||
|
else {
|
||
|
ws += `\n${ctx.indent}`;
|
||
|
}
|
||
|
}
|
||
|
else if (!explicitKey && isCollection(value)) {
|
||
|
const vs0 = valueStr[0];
|
||
|
const nl0 = valueStr.indexOf('\n');
|
||
|
const hasNewline = nl0 !== -1;
|
||
|
const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0;
|
||
|
if (hasNewline || !flow) {
|
||
|
let hasPropsLine = false;
|
||
|
if (hasNewline && (vs0 === '&' || vs0 === '!')) {
|
||
|
let sp0 = valueStr.indexOf(' ');
|
||
|
if (vs0 === '&' &&
|
||
|
sp0 !== -1 &&
|
||
|
sp0 < nl0 &&
|
||
|
valueStr[sp0 + 1] === '!') {
|
||
|
sp0 = valueStr.indexOf(' ', sp0 + 1);
|
||
|
}
|
||
|
if (sp0 === -1 || nl0 < sp0)
|
||
|
hasPropsLine = true;
|
||
|
}
|
||
|
if (!hasPropsLine)
|
||
|
ws = `\n${ctx.indent}`;
|
||
|
}
|
||
|
}
|
||
|
else if (valueStr === '' || valueStr[0] === '\n') {
|
||
|
ws = '';
|
||
|
}
|
||
|
str += ws + valueStr;
|
||
|
if (ctx.inFlow) {
|
||
|
if (valueCommentDone && onComment)
|
||
|
onComment();
|
||
|
}
|
||
|
else if (valueComment && !valueCommentDone) {
|
||
|
str += lineComment(str, ctx.indent, commentString(valueComment));
|
||
|
}
|
||
|
else if (chompKeep && onChompKeep) {
|
||
|
onChompKeep();
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
export { stringifyPair };
|