You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
4.0 KiB
122 lines
4.0 KiB
import { anchorIsValid } from '../doc/anchors.js'; |
|
import { isPair, isAlias, isNode, isScalar, isCollection } from '../nodes/Node.js'; |
|
import { stringifyComment } from './stringifyComment.js'; |
|
import { stringifyString } from './stringifyString.js'; |
|
|
|
function createStringifyContext(doc, options) { |
|
const opt = Object.assign({ |
|
blockQuote: true, |
|
commentString: stringifyComment, |
|
defaultKeyType: null, |
|
defaultStringType: 'PLAIN', |
|
directives: null, |
|
doubleQuotedAsJSON: false, |
|
doubleQuotedMinMultiLineLength: 40, |
|
falseStr: 'false', |
|
indentSeq: true, |
|
lineWidth: 80, |
|
minContentWidth: 20, |
|
nullStr: 'null', |
|
simpleKeys: false, |
|
singleQuote: null, |
|
trueStr: 'true', |
|
verifyAliasOrder: true |
|
}, doc.schema.toStringOptions, options); |
|
let inFlow; |
|
switch (opt.collectionStyle) { |
|
case 'block': |
|
inFlow = false; |
|
break; |
|
case 'flow': |
|
inFlow = true; |
|
break; |
|
default: |
|
inFlow = null; |
|
} |
|
return { |
|
anchors: new Set(), |
|
doc, |
|
indent: '', |
|
indentStep: typeof opt.indent === 'number' ? ' '.repeat(opt.indent) : ' ', |
|
inFlow, |
|
options: opt |
|
}; |
|
} |
|
function getTagObject(tags, item) { |
|
if (item.tag) { |
|
const match = tags.filter(t => t.tag === item.tag); |
|
if (match.length > 0) |
|
return match.find(t => t.format === item.format) ?? match[0]; |
|
} |
|
let tagObj = undefined; |
|
let obj; |
|
if (isScalar(item)) { |
|
obj = item.value; |
|
const match = tags.filter(t => t.identify?.(obj)); |
|
tagObj = |
|
match.find(t => t.format === item.format) ?? match.find(t => !t.format); |
|
} |
|
else { |
|
obj = item; |
|
tagObj = tags.find(t => t.nodeClass && obj instanceof t.nodeClass); |
|
} |
|
if (!tagObj) { |
|
const name = obj?.constructor?.name ?? typeof obj; |
|
throw new Error(`Tag not resolved for ${name} value`); |
|
} |
|
return tagObj; |
|
} |
|
// needs to be called before value stringifier to allow for circular anchor refs |
|
function stringifyProps(node, tagObj, { anchors, doc }) { |
|
if (!doc.directives) |
|
return ''; |
|
const props = []; |
|
const anchor = (isScalar(node) || isCollection(node)) && node.anchor; |
|
if (anchor && anchorIsValid(anchor)) { |
|
anchors.add(anchor); |
|
props.push(`&${anchor}`); |
|
} |
|
const tag = node.tag ? node.tag : tagObj.default ? null : tagObj.tag; |
|
if (tag) |
|
props.push(doc.directives.tagString(tag)); |
|
return props.join(' '); |
|
} |
|
function stringify(item, ctx, onComment, onChompKeep) { |
|
if (isPair(item)) |
|
return item.toString(ctx, onComment, onChompKeep); |
|
if (isAlias(item)) { |
|
if (ctx.doc.directives) |
|
return item.toString(ctx); |
|
if (ctx.resolvedAliases?.has(item)) { |
|
throw new TypeError(`Cannot stringify circular structure without alias nodes`); |
|
} |
|
else { |
|
if (ctx.resolvedAliases) |
|
ctx.resolvedAliases.add(item); |
|
else |
|
ctx.resolvedAliases = new Set([item]); |
|
item = item.resolve(ctx.doc); |
|
} |
|
} |
|
let tagObj = undefined; |
|
const node = isNode(item) |
|
? item |
|
: ctx.doc.createNode(item, { onTagObj: o => (tagObj = o) }); |
|
if (!tagObj) |
|
tagObj = getTagObject(ctx.doc.schema.tags, node); |
|
const props = stringifyProps(node, tagObj, ctx); |
|
if (props.length > 0) |
|
ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; |
|
const str = typeof tagObj.stringify === 'function' |
|
? tagObj.stringify(node, ctx, onComment, onChompKeep) |
|
: isScalar(node) |
|
? stringifyString(node, ctx, onComment, onChompKeep) |
|
: node.toString(ctx, onComment, onChompKeep); |
|
if (!props) |
|
return str; |
|
return isScalar(node) || str[0] === '{' || str[0] === '[' |
|
? `${props} ${str}` |
|
: `${props}\n${ctx.indent}${str}`; |
|
} |
|
|
|
export { createStringifyContext, stringify };
|
|
|