yaml
Advanced tools
Comparing version 2.0.0-9 to 2.0.0-10
@@ -10,2 +10,3 @@ import { Document } from '../doc/Document.js'; | ||
const ctx = { | ||
atRoot: true, | ||
directives: doc.directives, | ||
@@ -12,0 +13,0 @@ options: doc.options, |
@@ -1,2 +0,2 @@ | ||
import { isScalar, SCALAR } from '../nodes/Node.js'; | ||
import { SCALAR, isScalar } from '../nodes/Node.js'; | ||
import { Scalar } from '../nodes/Scalar.js'; | ||
@@ -15,3 +15,5 @@ import { resolveBlockScalar } from './resolve-block-scalar.js'; | ||
? findScalarTagByName(ctx.schema, value, tagName, tagToken, onError) | ||
: findScalarTagByTest(ctx.schema, value, token.type === 'scalar'); | ||
: token.type === 'scalar' | ||
? findScalarTagByTest(ctx, value, token, onError) | ||
: ctx.schema[SCALAR]; | ||
let scalar; | ||
@@ -65,13 +67,17 @@ try { | ||
} | ||
function findScalarTagByTest(schema, value, apply) { | ||
var _a; | ||
if (apply) { | ||
for (const tag of schema.tags) { | ||
if (tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value))) | ||
return tag; | ||
function findScalarTagByTest({ directives, schema }, value, token, onError) { | ||
const tag = schema.tags.find(tag => { var _a; return tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)); }) || schema[SCALAR]; | ||
if (schema.compat) { | ||
const compat = schema.compat.find(tag => { var _a; return tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)); }) || | ||
schema[SCALAR]; | ||
if (tag.tag !== compat.tag) { | ||
const ts = directives.tagString(tag.tag); | ||
const cs = directives.tagString(compat.tag); | ||
const msg = `Value may be parsed as either ${ts} or ${cs}`; | ||
onError(token, 'TAG_RESOLVE_FAILED', msg, true); | ||
} | ||
} | ||
return schema[SCALAR]; | ||
return tag; | ||
} | ||
export { composeScalar }; |
@@ -5,2 +5,3 @@ import { Pair } from '../nodes/Pair.js'; | ||
import { containsNewline } from './util-contains-newline.js'; | ||
import { flowIndentCheck } from './util-flow-indent-check.js'; | ||
import { mapIncludes } from './util-map-includes.js'; | ||
@@ -12,2 +13,4 @@ | ||
const map = new YAMLMap(ctx.schema); | ||
if (ctx.atRoot) | ||
ctx.atRoot = false; | ||
let offset = bm.offset; | ||
@@ -53,2 +56,4 @@ for (const collItem of bm.items) { | ||
: composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); | ||
if (ctx.schema.compat) | ||
flowIndentCheck(bm.indent, key, onError); | ||
if (mapIncludes(ctx, map.items, keyNode)) | ||
@@ -77,2 +82,4 @@ onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique'); | ||
: composeEmptyNode(ctx, offset, sep, null, valueProps, onError); | ||
if (ctx.schema.compat) | ||
flowIndentCheck(bm.indent, value, onError); | ||
offset = valueNode.range[2]; | ||
@@ -79,0 +86,0 @@ const pair = new Pair(keyNode, valueNode); |
import { YAMLSeq } from '../nodes/YAMLSeq.js'; | ||
import { resolveProps } from './resolve-props.js'; | ||
import { flowIndentCheck } from './util-flow-indent-check.js'; | ||
function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError) { | ||
const seq = new YAMLSeq(ctx.schema); | ||
if (ctx.atRoot) | ||
ctx.atRoot = false; | ||
let offset = bs.offset; | ||
@@ -33,2 +36,4 @@ for (const { start, value } of bs.items) { | ||
: composeEmptyNode(ctx, offset, start, null, props, onError); | ||
if (ctx.schema.compat) | ||
flowIndentCheck(bs.indent, value, onError); | ||
offset = node.range[2]; | ||
@@ -35,0 +40,0 @@ seq.items.push(node); |
@@ -19,3 +19,6 @@ import { isPair } from '../nodes/Node.js'; | ||
coll.flow = true; | ||
let offset = fc.offset; | ||
const atRoot = ctx.atRoot; | ||
if (atRoot) | ||
ctx.atRoot = false; | ||
let offset = fc.offset + fc.start.source.length; | ||
for (let i = 0; i < fc.items.length; ++i) { | ||
@@ -44,2 +47,3 @@ const collItem = fc.items[i]; | ||
} | ||
offset = props.end; | ||
continue; | ||
@@ -174,3 +178,7 @@ } | ||
else { | ||
onError(offset + 1, 'MISSING_CHAR', `Expected ${fcName} to end with ${expectedEnd}`); | ||
const name = fcName[0].toUpperCase() + fcName.substring(1); | ||
const msg = atRoot | ||
? `${name} must end with a ${expectedEnd}` | ||
: `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; | ||
onError(offset, atRoot ? 'MISSING_CHAR' : 'BAD_INDENT', msg); | ||
if (ce && ce.source.length !== 1) | ||
@@ -177,0 +185,0 @@ ee.unshift(ce); |
@@ -84,2 +84,4 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) { | ||
onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`); | ||
if (found) | ||
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow || 'collection'}`); | ||
found = token; | ||
@@ -86,0 +88,0 @@ atNewline = false; |
@@ -65,3 +65,4 @@ import { Alias } from '../nodes/Alias.js'; | ||
copy.options = Object.assign({}, this.options); | ||
copy.directives = this.directives.clone(); | ||
if (this.directives) | ||
copy.directives = this.directives.clone(); | ||
copy.schema = this.schema.clone(); | ||
@@ -233,22 +234,43 @@ copy.contents = isNode(this.contents) | ||
* Change the YAML version and schema used by the document. | ||
* A `null` version disables support for directives, explicit tags, anchors, and aliases. | ||
* It also requires the `schema` option to be given as a `Schema` instance value. | ||
* | ||
* Overrides all previously set schema options | ||
* Overrides all previously set schema options. | ||
*/ | ||
setSchema(version, options) { | ||
let _options; | ||
switch (String(version)) { | ||
setSchema(version, options = {}) { | ||
if (typeof version === 'number') | ||
version = String(version); | ||
let opt; | ||
switch (version) { | ||
case '1.1': | ||
this.directives.yaml.version = '1.1'; | ||
_options = Object.assign({ merge: true, resolveKnownTags: false, schema: 'yaml-1.1' }, options); | ||
if (this.directives) | ||
this.directives.yaml.version = '1.1'; | ||
else | ||
this.directives = new Directives({ version: '1.1' }); | ||
opt = { merge: true, resolveKnownTags: false, schema: 'yaml-1.1' }; | ||
break; | ||
case '1.2': | ||
this.directives.yaml.version = '1.2'; | ||
_options = Object.assign({ merge: false, resolveKnownTags: true, schema: 'core' }, options); | ||
if (this.directives) | ||
this.directives.yaml.version = '1.2'; | ||
else | ||
this.directives = new Directives({ version: '1.2' }); | ||
opt = { merge: false, resolveKnownTags: true, schema: 'core' }; | ||
break; | ||
case null: | ||
if (this.directives) | ||
delete this.directives; | ||
opt = null; | ||
break; | ||
default: { | ||
const sv = JSON.stringify(version); | ||
throw new Error(`Expected '1.1' or '1.2' as version, but found: ${sv}`); | ||
throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); | ||
} | ||
} | ||
this.schema = new Schema(_options); | ||
// Not using `instanceof Schema` to allow for duck typing | ||
if (options.schema instanceof Object) | ||
this.schema = options.schema; | ||
else if (opt) | ||
this.schema = new Schema(Object.assign(opt, options)); | ||
else | ||
throw new Error(`With a null YAML version, the { schema: Schema } option is required`); | ||
} | ||
@@ -255,0 +277,0 @@ // json & jsonArg are only used from toJSON() |
@@ -107,3 +107,3 @@ import { stringifyCollection } from '../stringify/stringifyCollection.js'; | ||
return stringifyCollection(this, ctx, { | ||
blockItem: n => n.str, | ||
blockItemPrefix: '', | ||
flowChars: { start: '{', end: '}' }, | ||
@@ -110,0 +110,0 @@ itemIndent: ctx.indent || '', |
@@ -88,3 +88,3 @@ import { stringifyCollection } from '../stringify/stringifyCollection.js'; | ||
return stringifyCollection(this, ctx, { | ||
blockItem: n => (n.comment ? n.str : `- ${n.str}`), | ||
blockItemPrefix: '- ', | ||
flowChars: { start: '[', end: ']' }, | ||
@@ -91,0 +91,0 @@ itemIndent: (ctx.indent || '') + ' ', |
@@ -419,2 +419,3 @@ import { BOM, DOCUMENT, FLOW_END, SCALAR } from './cst.js'; | ||
if (this.flowKey || isEmpty(next) || next === ',') { | ||
this.flowKey = false; | ||
yield* this.pushCount(1); | ||
@@ -627,2 +628,4 @@ yield* this.pushSpaces(true); | ||
this.indentNext = this.indentValue + 1; | ||
else if (this.flowKey) | ||
this.flowKey = false; | ||
return ((yield* this.pushCount(1)) + | ||
@@ -629,0 +632,0 @@ (yield* this.pushSpaces(true)) + |
@@ -206,2 +206,3 @@ import { tokenType } from './cst.js'; | ||
case 'doc-mode': | ||
case 'flow-error-end': | ||
return; | ||
@@ -279,5 +280,10 @@ default: | ||
const top = this.peek(1); | ||
// For these, parent indent is needed instead of own | ||
if (token.type === 'block-scalar' || token.type === 'flow-collection') | ||
token.indent = 'indent' in top ? top.indent : -1; | ||
if (token.type === 'block-scalar') { | ||
// Block scalars use their parent rather than header indent | ||
token.indent = 'indent' in top ? top.indent : 0; | ||
} | ||
else if (token.type === 'flow-collection' && top.type === 'document') { | ||
// Ignore all indent for top-level flow collections | ||
token.indent = 0; | ||
} | ||
if (token.type === 'flow-collection') | ||
@@ -284,0 +290,0 @@ fixFlowSeqItems(token); |
@@ -9,6 +9,4 @@ import { Composer } from './compose/composer.js'; | ||
function parseOptions(options) { | ||
const prettyErrors = !options || options.prettyErrors !== false; | ||
const lineCounter = (options && options.lineCounter) || | ||
(prettyErrors && new LineCounter()) || | ||
null; | ||
const prettyErrors = options.prettyErrors !== false; | ||
const lineCounter = options.lineCounter || (prettyErrors && new LineCounter()) || null; | ||
return { lineCounter, prettyErrors }; | ||
@@ -15,0 +13,0 @@ } |
@@ -23,3 +23,6 @@ import { Scalar } from '../../nodes/Scalar.js'; | ||
resolve: str => parseFloat(str), | ||
stringify: ({ value }) => Number(value).toExponential() | ||
stringify(node) { | ||
const num = Number(node.value); | ||
return isFinite(num) ? num.toExponential() : stringifyNumber(node); | ||
} | ||
}; | ||
@@ -26,0 +29,0 @@ const float = { |
@@ -5,11 +5,17 @@ import { MAP, SCALAR, SEQ } from '../nodes/Node.js'; | ||
import { string } from './common/string.js'; | ||
import { coreKnownTags, getTags } from './tags.js'; | ||
import { getTags, coreKnownTags } from './tags.js'; | ||
const sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; | ||
class Schema { | ||
constructor({ customTags, merge, resolveKnownTags, schema, sortMapEntries }) { | ||
constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { | ||
this.compat = Array.isArray(compat) | ||
? getTags(compat, 'compat') | ||
: compat | ||
? getTags(null, compat) | ||
: null; | ||
this.merge = !!merge; | ||
this.name = schema || 'core'; | ||
this.name = (typeof schema === 'string' && schema) || 'core'; | ||
this.knownTags = resolveKnownTags ? coreKnownTags : {}; | ||
this.tags = getTags(customTags, this.name); | ||
this.toStringOptions = toStringDefaults || null; | ||
Object.defineProperty(this, MAP, { value: map }); | ||
@@ -16,0 +22,0 @@ Object.defineProperty(this, SCALAR, { value: string }); |
@@ -17,9 +17,9 @@ import { map } from './common/map.js'; | ||
const schemas = { | ||
core: schema, | ||
failsafe: [map, seq, string], | ||
json: schema$1, | ||
yaml11: schema$2, | ||
'yaml-1.1': schema$2 | ||
}; | ||
const schemas = new Map([ | ||
['core', schema], | ||
['failsafe', [map, seq, string]], | ||
['json', schema$1], | ||
['yaml11', schema$2], | ||
['yaml-1.1', schema$2] | ||
]); | ||
const tagsByName = { | ||
@@ -52,3 +52,3 @@ binary, | ||
function getTags(customTags, schemaName) { | ||
let tags = schemas[schemaName]; | ||
let tags = schemas.get(schemaName); | ||
if (!tags) { | ||
@@ -58,3 +58,3 @@ if (Array.isArray(customTags)) | ||
else { | ||
const keys = Object.keys(schemas) | ||
const keys = Array.from(schemas.keys()) | ||
.filter(key => key !== 'yaml11') | ||
@@ -61,0 +61,0 @@ .map(key => JSON.stringify(key)) |
@@ -23,3 +23,6 @@ import { Scalar } from '../../nodes/Scalar.js'; | ||
resolve: (str) => parseFloat(str.replace(/_/g, '')), | ||
stringify: ({ value }) => Number(value).toExponential() | ||
stringify(node) { | ||
const num = Number(node.value); | ||
return isFinite(num) ? num.toExponential() : stringifyNumber(node); | ||
} | ||
}; | ||
@@ -26,0 +29,0 @@ const float = { |
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'; | ||
const createStringifyContext = (doc, options) => ({ | ||
anchors: new Set(), | ||
doc, | ||
indent: '', | ||
indentStep: typeof options.indent === 'number' ? ' '.repeat(options.indent) : ' ', | ||
options: Object.assign({ | ||
function createStringifyContext(doc, options) { | ||
const opt = Object.assign({ | ||
blockQuote: true, | ||
commentString: stringifyComment, | ||
defaultKeyType: null, | ||
@@ -26,4 +24,23 @@ defaultStringType: 'PLAIN', | ||
verifyAliasOrder: true | ||
}, options) | ||
}); | ||
}, 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) { | ||
@@ -56,2 +73,4 @@ if (item.tag) { | ||
function stringifyProps(node, tagObj, { anchors, doc }) { | ||
if (!doc.directives) | ||
return ''; | ||
const props = []; | ||
@@ -63,15 +82,25 @@ const anchor = (isScalar(node) || isCollection(node)) && node.anchor; | ||
} | ||
if (node.tag) { | ||
props.push(doc.directives.tagString(node.tag)); | ||
} | ||
else if (!tagObj.default) { | ||
props.push(doc.directives.tagString(tagObj.tag)); | ||
} | ||
const 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) { | ||
var _a; | ||
if (isPair(item)) | ||
return item.toString(ctx, onComment, onChompKeep); | ||
if (isAlias(item)) | ||
return item.toString(ctx); | ||
if (isAlias(item)) { | ||
if (ctx.doc.directives) | ||
return item.toString(ctx); | ||
if ((_a = ctx.resolvedAliases) === null || _a === void 0 ? void 0 : _a.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; | ||
@@ -78,0 +107,0 @@ const node = isNode(item) |
import { Collection } from '../nodes/Collection.js'; | ||
import { isNode, isPair } from '../nodes/Node.js'; | ||
import { stringify } from './stringify.js'; | ||
import { addComment, stringifyComment } from './stringifyComment.js'; | ||
import { lineComment, indentComment } from './stringifyComment.js'; | ||
function stringifyCollection({ comment, flow, items }, ctx, { blockItem, flowChars, itemIndent, onChompKeep, onComment }) { | ||
const { indent, indentStep } = ctx; | ||
const inFlow = flow || ctx.inFlow; | ||
if (inFlow) | ||
itemIndent += indentStep; | ||
ctx = Object.assign({}, ctx, { indent: itemIndent, inFlow, type: null }); | ||
let singleLineOutput = true; | ||
function stringifyCollection(collection, ctx, options) { | ||
var _a; | ||
const flow = (_a = ctx.inFlow) !== null && _a !== void 0 ? _a : collection.flow; | ||
const stringify = flow ? stringifyFlowCollection : stringifyBlockCollection; | ||
return stringify(collection, ctx, options); | ||
} | ||
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { | ||
const { indent, options: { commentString } } = ctx; | ||
const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); | ||
let chompKeep = false; // flag for the preceding node's status | ||
const nodes = items.reduce((nodes, item, i) => { | ||
const lines = []; | ||
for (let i = 0; i < items.length; ++i) { | ||
const item = items[i]; | ||
let comment = null; | ||
if (isNode(item)) { | ||
if (!chompKeep && item.spaceBefore) | ||
nodes.push({ comment: true, str: '' }); | ||
let cb = item.commentBefore; | ||
if (cb && chompKeep) | ||
cb = cb.replace(/^\n+/, ''); | ||
if (cb) { | ||
if (/^\n+$/.test(cb)) | ||
cb = cb.substring(1); | ||
// This match will always succeed on a non-empty string | ||
for (const line of cb.match(/^.*$/gm)) { | ||
const str = line === ' ' ? '#' : line ? `#${line}` : ''; | ||
nodes.push({ comment: true, str }); | ||
} | ||
} | ||
if (item.comment) { | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, item.commentBefore, chompKeep); | ||
if (item.comment) | ||
comment = item.comment; | ||
singleLineOutput = false; | ||
} | ||
} | ||
@@ -40,84 +31,123 @@ else if (isPair(item)) { | ||
if (!chompKeep && ik.spaceBefore) | ||
nodes.push({ comment: true, str: '' }); | ||
let cb = ik.commentBefore; | ||
if (cb && chompKeep) | ||
cb = cb.replace(/^\n+/, ''); | ||
if (cb) { | ||
if (/^\n+$/.test(cb)) | ||
cb = cb.substring(1); | ||
// This match will always succeed on a non-empty string | ||
for (const line of cb.match(/^.*$/gm)) { | ||
const str = line === ' ' ? '#' : line ? `#${line}` : ''; | ||
nodes.push({ comment: true, str }); | ||
} | ||
} | ||
if (ik.comment) | ||
singleLineOutput = false; | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); | ||
} | ||
if (inFlow) { | ||
const iv = isNode(item.value) ? item.value : null; | ||
if (iv) { | ||
if (iv.comment) | ||
comment = iv.comment; | ||
if (iv.comment || iv.commentBefore) | ||
singleLineOutput = false; | ||
} | ||
else if (item.value == null && ik && ik.comment) { | ||
comment = ik.comment; | ||
} | ||
} | ||
} | ||
chompKeep = false; | ||
let str = stringify(item, ctx, () => (comment = null), () => (chompKeep = true)); | ||
if (inFlow && i < items.length - 1) | ||
str += ','; | ||
str = addComment(str, itemIndent, comment); | ||
if (chompKeep && (comment || inFlow)) | ||
let str = stringify(item, itemCtx, () => (comment = null), () => (chompKeep = true)); | ||
if (comment) | ||
str += lineComment(str, itemIndent, commentString(comment)); | ||
if (chompKeep && comment) | ||
chompKeep = false; | ||
nodes.push({ comment: false, str }); | ||
return nodes; | ||
}, []); | ||
lines.push(blockItemPrefix + str); | ||
} | ||
let str; | ||
if (nodes.length === 0) { | ||
if (lines.length === 0) { | ||
str = flowChars.start + flowChars.end; | ||
} | ||
else if (inFlow) { | ||
const { start, end } = flowChars; | ||
const strings = nodes.map(n => n.str); | ||
let singleLineLength = 2; | ||
for (const node of nodes) { | ||
if (node.comment || node.str.includes('\n')) { | ||
singleLineOutput = false; | ||
break; | ||
else { | ||
str = lines[0]; | ||
for (let i = 1; i < lines.length; ++i) { | ||
const line = lines[i]; | ||
str += line ? `\n${indent}${line}` : '\n'; | ||
} | ||
} | ||
if (comment) { | ||
str += '\n' + indentComment(commentString(comment), indent); | ||
if (onComment) | ||
onComment(); | ||
} | ||
else if (chompKeep && onChompKeep) | ||
onChompKeep(); | ||
return str; | ||
} | ||
function stringifyFlowCollection({ comment, items }, ctx, { flowChars, itemIndent, onComment }) { | ||
const { indent, indentStep, options: { commentString } } = ctx; | ||
itemIndent += indentStep; | ||
const itemCtx = Object.assign({}, ctx, { | ||
indent: itemIndent, | ||
inFlow: true, | ||
type: null | ||
}); | ||
let reqNewline = false; | ||
let linesAtValue = 0; | ||
const lines = []; | ||
for (let i = 0; i < items.length; ++i) { | ||
const item = items[i]; | ||
let comment = null; | ||
if (isNode(item)) { | ||
if (item.spaceBefore) | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, item.commentBefore, false); | ||
if (item.comment) | ||
comment = item.comment; | ||
} | ||
else if (isPair(item)) { | ||
const ik = isNode(item.key) ? item.key : null; | ||
if (ik) { | ||
if (ik.spaceBefore) | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, ik.commentBefore, false); | ||
if (ik.comment) | ||
reqNewline = true; | ||
} | ||
singleLineLength += node.str.length + 2; | ||
const iv = isNode(item.value) ? item.value : null; | ||
if (iv) { | ||
if (iv.comment) | ||
comment = iv.comment; | ||
if (iv.commentBefore) | ||
reqNewline = true; | ||
} | ||
else if (item.value == null && ik && ik.comment) { | ||
comment = ik.comment; | ||
} | ||
} | ||
if (!singleLineOutput || | ||
singleLineLength > Collection.maxFlowStringSingleLineLength) { | ||
if (comment) | ||
reqNewline = true; | ||
let str = stringify(item, itemCtx, () => (comment = null)); | ||
if (i < items.length - 1) | ||
str += ','; | ||
if (comment) | ||
str += lineComment(str, itemIndent, commentString(comment)); | ||
if (!reqNewline && (lines.length > linesAtValue || str.includes('\n'))) | ||
reqNewline = true; | ||
lines.push(str); | ||
linesAtValue = lines.length; | ||
} | ||
let str; | ||
const { start, end } = flowChars; | ||
if (lines.length === 0) { | ||
str = start + end; | ||
} | ||
else { | ||
if (!reqNewline) { | ||
const len = lines.reduce((sum, line) => sum + line.length + 2, 2); | ||
reqNewline = len > Collection.maxFlowStringSingleLineLength; | ||
} | ||
if (reqNewline) { | ||
str = start; | ||
for (const s of strings) { | ||
str += s ? `\n${indentStep}${indent}${s}` : '\n'; | ||
} | ||
for (const line of lines) | ||
str += line ? `\n${indentStep}${indent}${line}` : '\n'; | ||
str += `\n${indent}${end}`; | ||
} | ||
else { | ||
str = `${start} ${strings.join(' ')} ${end}`; | ||
str = `${start} ${lines.join(' ')} ${end}`; | ||
} | ||
} | ||
else { | ||
const strings = nodes.map(blockItem); | ||
str = strings.shift() || ''; | ||
for (const s of strings) | ||
str += s ? `\n${indent}${s}` : '\n'; | ||
} | ||
if (comment) { | ||
str += '\n' + stringifyComment(comment, indent); | ||
str += lineComment(str, commentString(comment), indent); | ||
if (onComment) | ||
onComment(); | ||
} | ||
else if (chompKeep && onChompKeep) | ||
onChompKeep(); | ||
return str; | ||
} | ||
function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { | ||
if (comment && chompKeep) | ||
comment = comment.replace(/^\n+/, ''); | ||
if (comment) { | ||
const ic = indentComment(commentString(comment), indent); | ||
lines.push(ic.trimStart()); // Avoid double indent on first line | ||
} | ||
} | ||
export { stringifyCollection }; |
@@ -1,14 +0,18 @@ | ||
const stringifyComment = (comment, indent) => /^\n+$/.test(comment) | ||
? comment.substring(1) | ||
: comment.replace(/^(?!$)(?: $)?/gm, `${indent}#`); | ||
function addComment(str, indent, comment) { | ||
return !comment | ||
? str | ||
: comment.includes('\n') | ||
? `${str}\n` + stringifyComment(comment, indent) | ||
: str.endsWith(' ') | ||
? `${str}#${comment}` | ||
: `${str} #${comment}`; | ||
/** | ||
* Stringifies a comment. | ||
* | ||
* Empty comment lines are left empty, | ||
* lines consisting of a single space are replaced by `#`, | ||
* and all other lines are prefixed with a `#`. | ||
*/ | ||
const stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, '#'); | ||
function indentComment(comment, indent) { | ||
if (/^\n+$/.test(comment)) | ||
return comment.substring(1); | ||
return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; | ||
} | ||
const lineComment = (str, indent, comment) => comment.includes('\n') | ||
? '\n' + indentComment(comment, indent) | ||
: (str.endsWith(' ') ? '' : ' ') + comment; | ||
export { addComment, stringifyComment }; | ||
export { indentComment, lineComment, stringifyComment }; |
import { isNode } from '../nodes/Node.js'; | ||
import { createStringifyContext, stringify } from './stringify.js'; | ||
import { stringifyComment, addComment } from './stringifyComment.js'; | ||
import { indentComment, lineComment } from './stringifyComment.js'; | ||
@@ -8,3 +8,3 @@ function stringifyDocument(doc, options) { | ||
let hasDirectives = options.directives === true; | ||
if (options.directives !== false) { | ||
if (options.directives !== false && doc.directives) { | ||
const dir = doc.directives.toString(doc); | ||
@@ -20,8 +20,10 @@ if (dir) { | ||
lines.push('---'); | ||
const ctx = createStringifyContext(doc, options); | ||
const { commentString } = ctx.options; | ||
if (doc.commentBefore) { | ||
if (lines.length !== 1) | ||
lines.unshift(''); | ||
lines.unshift(stringifyComment(doc.commentBefore, '')); | ||
const cs = commentString(doc.commentBefore); | ||
lines.unshift(indentComment(cs, '')); | ||
} | ||
const ctx = createStringifyContext(doc, options); | ||
let chompKeep = false; | ||
@@ -33,4 +35,6 @@ let contentComment = null; | ||
lines.push(''); | ||
if (doc.contents.commentBefore) | ||
lines.push(stringifyComment(doc.contents.commentBefore, '')); | ||
if (doc.contents.commentBefore) { | ||
const cs = commentString(doc.contents.commentBefore); | ||
lines.push(indentComment(cs, '')); | ||
} | ||
// top-level block scalars need to be indented if followed by a comment | ||
@@ -43,3 +47,3 @@ ctx.forceBlockIndent = !!doc.comment; | ||
if (contentComment) | ||
body = addComment(body, '', contentComment); | ||
body += lineComment(body, '', commentString(contentComment)); | ||
if ((body[0] === '|' || body[0] === '>') && | ||
@@ -63,3 +67,3 @@ lines[lines.length - 1] === '---') { | ||
lines.push(''); | ||
lines.push(stringifyComment(dc, '')); | ||
lines.push(indentComment(commentString(dc), '')); | ||
} | ||
@@ -66,0 +70,0 @@ return lines.join('\n') + '\n'; |
import { isCollection, isNode, isScalar, isSeq } from '../nodes/Node.js'; | ||
import { Scalar } from '../nodes/Scalar.js'; | ||
import { stringify } from './stringify.js'; | ||
import { addComment, stringifyComment } from './stringifyComment.js'; | ||
import { lineComment, indentComment } from './stringifyComment.js'; | ||
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { | ||
const { allNullValues, doc, indent, indentStep, options: { indentSeq, simpleKeys } } = ctx; | ||
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; | ||
let keyComment = (isNode(key) && key.comment) || null; | ||
@@ -46,13 +46,22 @@ if (simpleKeys) { | ||
else if ((allNullValues && !simpleKeys) || (value == null && explicitKey)) { | ||
if (keyCommentDone) | ||
keyComment = null; | ||
if (chompKeep && !keyComment && onChompKeep) | ||
str = `? ${str}`; | ||
if (keyComment && !keyCommentDone) { | ||
str += lineComment(str, ctx.indent, commentString(keyComment)); | ||
} | ||
else if (chompKeep && onChompKeep) | ||
onChompKeep(); | ||
return addComment(`? ${str}`, ctx.indent, keyComment); | ||
return str; | ||
} | ||
if (keyCommentDone) | ||
keyComment = null; | ||
str = explicitKey | ||
? `? ${addComment(str, ctx.indent, keyComment)}\n${indent}:` | ||
: addComment(`${str}:`, ctx.indent, keyComment); | ||
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 vcb = ''; | ||
@@ -63,4 +72,6 @@ let valueComment = null; | ||
vcb = '\n'; | ||
if (value.commentBefore) | ||
vcb += `\n${stringifyComment(value.commentBefore, ctx.indent)}`; | ||
if (value.commentBefore) { | ||
const cs = commentString(value.commentBefore); | ||
vcb += `\n${indentComment(cs, ctx.indent)}`; | ||
} | ||
valueComment = value.comment; | ||
@@ -99,16 +110,16 @@ } | ||
ws = ''; | ||
str += ws + valueStr; | ||
if (ctx.inFlow) { | ||
if (valueCommentDone && onComment) | ||
onComment(); | ||
return str + ws + valueStr; | ||
} | ||
else { | ||
if (valueCommentDone) | ||
valueComment = null; | ||
if (chompKeep && !valueComment && onChompKeep) | ||
onChompKeep(); | ||
return addComment(str + ws + valueStr, ctx.indent, valueComment); | ||
else if (valueComment && !valueCommentDone) { | ||
str += lineComment(str, ctx.indent, commentString(valueComment)); | ||
} | ||
else if (chompKeep && onChompKeep) { | ||
onChompKeep(); | ||
} | ||
return str; | ||
} | ||
export { stringifyPair }; |
@@ -150,3 +150,3 @@ import { Scalar } from '../nodes/Scalar.js'; | ||
function blockString({ comment, type, value }, ctx, onComment, onChompKeep) { | ||
const { lineWidth, blockQuote } = ctx.options; | ||
const { blockQuote, commentString, lineWidth } = ctx.options; | ||
// 1. Block can't end in whitespace unless the last line is non-empty. | ||
@@ -216,3 +216,3 @@ // 2. Strings consisting of only whitespace are best rendered explicitly. | ||
if (comment) { | ||
header += ' #' + comment.replace(/ ?[\r\n]+/g, ' '); | ||
header += ' ' + commentString(comment.replace(/ ?[\r\n]+/g, ' ')); | ||
if (onComment) | ||
@@ -234,3 +234,2 @@ onComment(); | ||
function plainString(item, ctx, onComment, onChompKeep) { | ||
var _a; | ||
const { type, value } = item; | ||
@@ -270,8 +269,6 @@ const { actualString, implicitKey, indent, inFlow } = ctx; | ||
if (actualString) { | ||
for (const tag of ctx.doc.schema.tags) { | ||
if (tag.default && | ||
tag.tag !== 'tag:yaml.org,2002:str' && | ||
((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(str))) | ||
return quotedString(value, ctx); | ||
} | ||
const test = (tag) => { var _a; return tag.default && tag.tag !== 'tag:yaml.org,2002:str' && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(str)); }; | ||
const { compat, tags } = ctx.doc.schema; | ||
if (tags.some(test) || (compat === null || compat === void 0 ? void 0 : compat.some(test))) | ||
return quotedString(value, ctx); | ||
} | ||
@@ -278,0 +275,0 @@ return implicitKey |
@@ -12,2 +12,3 @@ 'use strict'; | ||
const ctx = { | ||
atRoot: true, | ||
directives: doc.directives, | ||
@@ -14,0 +15,0 @@ options: doc.options, |
@@ -8,2 +8,3 @@ import type { Directives } from '../doc/directives.js'; | ||
export interface ComposeContext { | ||
atRoot: boolean; | ||
directives: Directives; | ||
@@ -10,0 +11,0 @@ options: Readonly<Required<Omit<ParseOptions, 'lineCounter'>>>; |
@@ -17,3 +17,5 @@ 'use strict'; | ||
? findScalarTagByName(ctx.schema, value, tagName, tagToken, onError) | ||
: findScalarTagByTest(ctx.schema, value, token.type === 'scalar'); | ||
: token.type === 'scalar' | ||
? findScalarTagByTest(ctx, value, token, onError) | ||
: ctx.schema[Node.SCALAR]; | ||
let scalar; | ||
@@ -67,13 +69,17 @@ try { | ||
} | ||
function findScalarTagByTest(schema, value, apply) { | ||
var _a; | ||
if (apply) { | ||
for (const tag of schema.tags) { | ||
if (tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value))) | ||
return tag; | ||
function findScalarTagByTest({ directives, schema }, value, token, onError) { | ||
const tag = schema.tags.find(tag => { var _a; return tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)); }) || schema[Node.SCALAR]; | ||
if (schema.compat) { | ||
const compat = schema.compat.find(tag => { var _a; return tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)); }) || | ||
schema[Node.SCALAR]; | ||
if (tag.tag !== compat.tag) { | ||
const ts = directives.tagString(tag.tag); | ||
const cs = directives.tagString(compat.tag); | ||
const msg = `Value may be parsed as either ${ts} or ${cs}`; | ||
onError(token, 'TAG_RESOLVE_FAILED', msg, true); | ||
} | ||
} | ||
return schema[Node.SCALAR]; | ||
return tag; | ||
} | ||
exports.composeScalar = composeScalar; |
@@ -7,2 +7,3 @@ 'use strict'; | ||
var utilContainsNewline = require('./util-contains-newline.js'); | ||
var utilFlowIndentCheck = require('./util-flow-indent-check.js'); | ||
var utilMapIncludes = require('./util-map-includes.js'); | ||
@@ -14,2 +15,4 @@ | ||
const map = new YAMLMap.YAMLMap(ctx.schema); | ||
if (ctx.atRoot) | ||
ctx.atRoot = false; | ||
let offset = bm.offset; | ||
@@ -55,2 +58,4 @@ for (const collItem of bm.items) { | ||
: composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); | ||
if (ctx.schema.compat) | ||
utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); | ||
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) | ||
@@ -79,2 +84,4 @@ onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique'); | ||
: composeEmptyNode(ctx, offset, sep, null, valueProps, onError); | ||
if (ctx.schema.compat) | ||
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); | ||
offset = valueNode.range[2]; | ||
@@ -81,0 +88,0 @@ const pair = new Pair.Pair(keyNode, valueNode); |
@@ -5,5 +5,8 @@ 'use strict'; | ||
var resolveProps = require('./resolve-props.js'); | ||
var utilFlowIndentCheck = require('./util-flow-indent-check.js'); | ||
function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError) { | ||
const seq = new YAMLSeq.YAMLSeq(ctx.schema); | ||
if (ctx.atRoot) | ||
ctx.atRoot = false; | ||
let offset = bs.offset; | ||
@@ -36,2 +39,4 @@ for (const { start, value } of bs.items) { | ||
: composeEmptyNode(ctx, offset, start, null, props, onError); | ||
if (ctx.schema.compat) | ||
utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); | ||
offset = node.range[2]; | ||
@@ -38,0 +43,0 @@ seq.items.push(node); |
@@ -21,3 +21,6 @@ 'use strict'; | ||
coll.flow = true; | ||
let offset = fc.offset; | ||
const atRoot = ctx.atRoot; | ||
if (atRoot) | ||
ctx.atRoot = false; | ||
let offset = fc.offset + fc.start.source.length; | ||
for (let i = 0; i < fc.items.length; ++i) { | ||
@@ -46,2 +49,3 @@ const collItem = fc.items[i]; | ||
} | ||
offset = props.end; | ||
continue; | ||
@@ -176,3 +180,7 @@ } | ||
else { | ||
onError(offset + 1, 'MISSING_CHAR', `Expected ${fcName} to end with ${expectedEnd}`); | ||
const name = fcName[0].toUpperCase() + fcName.substring(1); | ||
const msg = atRoot | ||
? `${name} must end with a ${expectedEnd}` | ||
: `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; | ||
onError(offset, atRoot ? 'MISSING_CHAR' : 'BAD_INDENT', msg); | ||
if (ce && ce.source.length !== 1) | ||
@@ -179,0 +187,0 @@ ee.unshift(ce); |
@@ -86,2 +86,4 @@ 'use strict'; | ||
onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`); | ||
if (found) | ||
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow || 'collection'}`); | ||
found = token; | ||
@@ -88,0 +90,0 @@ atNewline = false; |
@@ -14,2 +14,3 @@ import type { YAMLError, YAMLWarning } from '../errors.js'; | ||
interface Parsed<T extends ParsedNode = ParsedNode> extends Document<T> { | ||
directives: Directives; | ||
range: Range; | ||
@@ -26,3 +27,3 @@ } | ||
contents: T | null; | ||
directives: Directives; | ||
directives?: Directives; | ||
/** Errors encountered during parsing. */ | ||
@@ -121,6 +122,8 @@ errors: YAMLError[]; | ||
* Change the YAML version and schema used by the document. | ||
* A `null` version disables support for directives, explicit tags, anchors, and aliases. | ||
* It also requires the `schema` option to be given as a `Schema` instance value. | ||
* | ||
* Overrides all previously set schema options | ||
* Overrides all previously set schema options. | ||
*/ | ||
setSchema(version: '1.1' | '1.2', options?: SchemaOptions): void; | ||
setSchema(version: '1.1' | '1.2' | null, options?: SchemaOptions): void; | ||
/** A plain JavaScript representation of the document `contents`. */ | ||
@@ -127,0 +130,0 @@ toJS(opt?: ToJSOptions & { |
@@ -67,3 +67,4 @@ 'use strict'; | ||
copy.options = Object.assign({}, this.options); | ||
copy.directives = this.directives.clone(); | ||
if (this.directives) | ||
copy.directives = this.directives.clone(); | ||
copy.schema = this.schema.clone(); | ||
@@ -235,22 +236,43 @@ copy.contents = Node.isNode(this.contents) | ||
* Change the YAML version and schema used by the document. | ||
* A `null` version disables support for directives, explicit tags, anchors, and aliases. | ||
* It also requires the `schema` option to be given as a `Schema` instance value. | ||
* | ||
* Overrides all previously set schema options | ||
* Overrides all previously set schema options. | ||
*/ | ||
setSchema(version, options) { | ||
let _options; | ||
switch (String(version)) { | ||
setSchema(version, options = {}) { | ||
if (typeof version === 'number') | ||
version = String(version); | ||
let opt; | ||
switch (version) { | ||
case '1.1': | ||
this.directives.yaml.version = '1.1'; | ||
_options = Object.assign({ merge: true, resolveKnownTags: false, schema: 'yaml-1.1' }, options); | ||
if (this.directives) | ||
this.directives.yaml.version = '1.1'; | ||
else | ||
this.directives = new directives.Directives({ version: '1.1' }); | ||
opt = { merge: true, resolveKnownTags: false, schema: 'yaml-1.1' }; | ||
break; | ||
case '1.2': | ||
this.directives.yaml.version = '1.2'; | ||
_options = Object.assign({ merge: false, resolveKnownTags: true, schema: 'core' }, options); | ||
if (this.directives) | ||
this.directives.yaml.version = '1.2'; | ||
else | ||
this.directives = new directives.Directives({ version: '1.2' }); | ||
opt = { merge: false, resolveKnownTags: true, schema: 'core' }; | ||
break; | ||
case null: | ||
if (this.directives) | ||
delete this.directives; | ||
opt = null; | ||
break; | ||
default: { | ||
const sv = JSON.stringify(version); | ||
throw new Error(`Expected '1.1' or '1.2' as version, but found: ${sv}`); | ||
throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); | ||
} | ||
} | ||
this.schema = new Schema.Schema(_options); | ||
// Not using `instanceof Schema` to allow for duck typing | ||
if (options.schema instanceof Object) | ||
this.schema = options.schema; | ||
else if (opt) | ||
this.schema = new Schema.Schema(Object.assign(opt, options)); | ||
else | ||
throw new Error(`With a null YAML version, the { schema: Schema } option is required`); | ||
} | ||
@@ -257,0 +279,0 @@ // json & jsonArg are only used from toJSON() |
@@ -109,3 +109,3 @@ 'use strict'; | ||
return stringifyCollection.stringifyCollection(this, ctx, { | ||
blockItem: n => n.str, | ||
blockItemPrefix: '', | ||
flowChars: { start: '{', end: '}' }, | ||
@@ -112,0 +112,0 @@ itemIndent: ctx.indent || '', |
@@ -90,3 +90,3 @@ 'use strict'; | ||
return stringifyCollection.stringifyCollection(this, ctx, { | ||
blockItem: n => (n.comment ? n.str : `- ${n.str}`), | ||
blockItemPrefix: '- ', | ||
flowChars: { start: '[', end: ']' }, | ||
@@ -93,0 +93,0 @@ itemIndent: (ctx.indent || '') + ' ', |
@@ -8,3 +8,3 @@ import type { Reviver } from './doc/applyReviver.js'; | ||
import type { LineCounter } from './parse/line-counter.js'; | ||
import type { SchemaName } from './schema/Schema.js'; | ||
import type { Schema } from './schema/Schema.js'; | ||
import type { Tags } from './schema/tags.js'; | ||
@@ -81,2 +81,10 @@ import type { CollectionTag, ScalarTag } from './schema/types.js'; | ||
/** | ||
* When parsing, warn about compatibility issues with the given schema. | ||
* When stringifying, use scalar styles that are parsed correctly | ||
* by the `compat` schema as well as the actual schema. | ||
* | ||
* Default: `null` | ||
*/ | ||
compat?: string | Tags | null; | ||
/** | ||
* Array of additional tags to include in the schema, or a function that may | ||
@@ -104,5 +112,14 @@ * modify the schema's base tag array. | ||
* | ||
* Default: `"core"` for YAML 1.2, `"yaml-1.1"` for earlier versions | ||
* The core library has built-in support for the following: | ||
* - `'failsafe'`: A minimal schema that parses all scalars as strings | ||
* - `'core'`: The YAML 1.2 core schema | ||
* - `'json'`: The YAML 1.2 JSON schema, with minimal rules for JSON compatibility | ||
* - `'yaml-1.1'`: The YAML 1.1 schema | ||
* | ||
* If using another (custom) schema, the `customTags` array needs to | ||
* fully define the schema's tags. | ||
* | ||
* Default: `'core'` for YAML 1.2, `'yaml-1.1'` for earlier versions | ||
*/ | ||
schema?: SchemaName; | ||
schema?: string | Schema; | ||
/** | ||
@@ -116,2 +133,6 @@ * When adding to or stringifying a map, sort the entries. | ||
sortMapEntries?: boolean | ((a: Pair, b: Pair) => number); | ||
/** | ||
* Override default values for `toString()` options. | ||
*/ | ||
toStringDefaults?: ToStringOptions; | ||
}; | ||
@@ -183,2 +204,19 @@ export declare type CreateNodeOptions = { | ||
/** | ||
* Enforce `'block'` or `'flow'` style on maps and sequences. | ||
* Empty collections will always be stringified as `{}` or `[]`. | ||
* | ||
* Default: `'any'`, allowing each node to set its style separately | ||
* with its `flow: boolean` (default `false`) property. | ||
*/ | ||
collectionStyle?: 'any' | 'block' | 'flow'; | ||
/** | ||
* Comment stringifier. | ||
* Output should be valid for the current schema. | ||
* | ||
* By default, empty comment lines are left empty, | ||
* lines consisting of a single space are replaced by `#`, | ||
* and all other lines are prefixed with a `#`. | ||
*/ | ||
commentString?: (comment: string) => string; | ||
/** | ||
* The default type of string literal used to stringify implicit key values. | ||
@@ -185,0 +223,0 @@ * Output may use other types if required to fully represent the value. |
@@ -421,2 +421,3 @@ 'use strict'; | ||
if (this.flowKey || isEmpty(next) || next === ',') { | ||
this.flowKey = false; | ||
yield* this.pushCount(1); | ||
@@ -629,2 +630,4 @@ yield* this.pushSpaces(true); | ||
this.indentNext = this.indentValue + 1; | ||
else if (this.flowKey) | ||
this.flowKey = false; | ||
return ((yield* this.pushCount(1)) + | ||
@@ -631,0 +634,0 @@ (yield* this.pushSpaces(true)) + |
@@ -210,2 +210,3 @@ 'use strict'; | ||
case 'doc-mode': | ||
case 'flow-error-end': | ||
return; | ||
@@ -283,5 +284,10 @@ default: | ||
const top = this.peek(1); | ||
// For these, parent indent is needed instead of own | ||
if (token.type === 'block-scalar' || token.type === 'flow-collection') | ||
token.indent = 'indent' in top ? top.indent : -1; | ||
if (token.type === 'block-scalar') { | ||
// Block scalars use their parent rather than header indent | ||
token.indent = 'indent' in top ? top.indent : 0; | ||
} | ||
else if (token.type === 'flow-collection' && top.type === 'document') { | ||
// Ignore all indent for top-level flow collections | ||
token.indent = 0; | ||
} | ||
if (token.type === 'flow-collection') | ||
@@ -288,0 +294,0 @@ fixFlowSeqItems(token); |
@@ -11,6 +11,4 @@ 'use strict'; | ||
function parseOptions(options) { | ||
const prettyErrors = !options || options.prettyErrors !== false; | ||
const lineCounter$1 = (options && options.lineCounter) || | ||
(prettyErrors && new lineCounter.LineCounter()) || | ||
null; | ||
const prettyErrors = options.prettyErrors !== false; | ||
const lineCounter$1 = options.lineCounter || (prettyErrors && new lineCounter.LineCounter()) || null; | ||
return { lineCounter: lineCounter$1, prettyErrors }; | ||
@@ -17,0 +15,0 @@ } |
@@ -25,3 +25,6 @@ 'use strict'; | ||
resolve: str => parseFloat(str), | ||
stringify: ({ value }) => Number(value).toExponential() | ||
stringify(node) { | ||
const num = Number(node.value); | ||
return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); | ||
} | ||
}; | ||
@@ -28,0 +31,0 @@ const float = { |
import { MAP, SCALAR, SEQ } from '../nodes/Node.js'; | ||
import type { Pair } from '../nodes/Pair.js'; | ||
import type { SchemaOptions } from '../options.js'; | ||
import type { SchemaOptions, ToStringOptions } from '../options.js'; | ||
import type { CollectionTag, ScalarTag } from './types.js'; | ||
export declare type SchemaName = 'core' | 'failsafe' | 'json' | 'yaml-1.1'; | ||
export declare class Schema { | ||
compat: Array<CollectionTag | ScalarTag> | null; | ||
knownTags: Record<string, CollectionTag | ScalarTag>; | ||
merge: boolean; | ||
name: SchemaName; | ||
name: string; | ||
sortMapEntries: ((a: Pair, b: Pair) => number) | null; | ||
tags: Array<CollectionTag | ScalarTag>; | ||
toStringOptions: Readonly<ToStringOptions> | null; | ||
[MAP]: CollectionTag; | ||
[SCALAR]: ScalarTag; | ||
[SEQ]: CollectionTag; | ||
constructor({ customTags, merge, resolveKnownTags, schema, sortMapEntries }: SchemaOptions); | ||
constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }: SchemaOptions); | ||
clone(): Schema; | ||
} |
@@ -11,7 +11,13 @@ 'use strict'; | ||
class Schema { | ||
constructor({ customTags, merge, resolveKnownTags, schema, sortMapEntries }) { | ||
constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { | ||
this.compat = Array.isArray(compat) | ||
? tags.getTags(compat, 'compat') | ||
: compat | ||
? tags.getTags(null, compat) | ||
: null; | ||
this.merge = !!merge; | ||
this.name = schema || 'core'; | ||
this.name = (typeof schema === 'string' && schema) || 'core'; | ||
this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; | ||
this.tags = tags.getTags(customTags, this.name); | ||
this.toStringOptions = toStringDefaults || null; | ||
Object.defineProperty(this, Node.MAP, { value: map.map }); | ||
@@ -18,0 +24,0 @@ Object.defineProperty(this, Node.SCALAR, { value: string.string }); |
import { SchemaOptions } from '../options.js'; | ||
import type { SchemaName } from './Schema.js'; | ||
import type { CollectionTag, ScalarTag } from './types.js'; | ||
@@ -40,3 +39,3 @@ declare const tagsByName: { | ||
}; | ||
export declare function getTags(customTags: SchemaOptions['customTags'] | undefined, schemaName: SchemaName): (ScalarTag | CollectionTag)[]; | ||
export declare function getTags(customTags: SchemaOptions['customTags'] | undefined, schemaName: string): (ScalarTag | CollectionTag)[]; | ||
export {}; |
@@ -19,9 +19,9 @@ 'use strict'; | ||
const schemas = { | ||
core: schema.schema, | ||
failsafe: [map.map, seq.seq, string.string], | ||
json: schema$1.schema, | ||
yaml11: schema$2.schema, | ||
'yaml-1.1': schema$2.schema | ||
}; | ||
const schemas = new Map([ | ||
['core', schema.schema], | ||
['failsafe', [map.map, seq.seq, string.string]], | ||
['json', schema$1.schema], | ||
['yaml11', schema$2.schema], | ||
['yaml-1.1', schema$2.schema] | ||
]); | ||
const tagsByName = { | ||
@@ -54,3 +54,3 @@ binary: binary.binary, | ||
function getTags(customTags, schemaName) { | ||
let tags = schemas[schemaName]; | ||
let tags = schemas.get(schemaName); | ||
if (!tags) { | ||
@@ -60,3 +60,3 @@ if (Array.isArray(customTags)) | ||
else { | ||
const keys = Object.keys(schemas) | ||
const keys = Array.from(schemas.keys()) | ||
.filter(key => key !== 'yaml11') | ||
@@ -63,0 +63,0 @@ .map(key => JSON.stringify(key)) |
@@ -25,3 +25,6 @@ 'use strict'; | ||
resolve: (str) => parseFloat(str.replace(/_/g, '')), | ||
stringify: ({ value }) => Number(value).toExponential() | ||
stringify(node) { | ||
const num = Number(node.value); | ||
return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); | ||
} | ||
}; | ||
@@ -28,0 +31,0 @@ const float = { |
import type { Document } from '../doc/Document.js'; | ||
import type { Alias } from '../nodes/Alias.js'; | ||
import type { ToStringOptions } from '../options.js'; | ||
@@ -13,7 +14,8 @@ export declare type StringifyContext = { | ||
indentAtStart?: number; | ||
inFlow?: boolean; | ||
inFlow: boolean | null; | ||
inStringifyKey?: boolean; | ||
options: Readonly<Required<Omit<ToStringOptions, 'indent'>>>; | ||
options: Readonly<Required<Omit<ToStringOptions, 'collectionStyle' | 'indent'>>>; | ||
resolvedAliases?: Set<Alias>; | ||
}; | ||
export declare const createStringifyContext: (doc: Document, options: ToStringOptions) => StringifyContext; | ||
export declare function createStringifyContext(doc: Document, options: ToStringOptions): StringifyContext; | ||
export declare function stringify(item: unknown, ctx: StringifyContext, onComment?: () => void, onChompKeep?: () => void): string; |
@@ -5,11 +5,9 @@ 'use strict'; | ||
var Node = require('../nodes/Node.js'); | ||
var stringifyComment = require('./stringifyComment.js'); | ||
var stringifyString = require('./stringifyString.js'); | ||
const createStringifyContext = (doc, options) => ({ | ||
anchors: new Set(), | ||
doc, | ||
indent: '', | ||
indentStep: typeof options.indent === 'number' ? ' '.repeat(options.indent) : ' ', | ||
options: Object.assign({ | ||
function createStringifyContext(doc, options) { | ||
const opt = Object.assign({ | ||
blockQuote: true, | ||
commentString: stringifyComment.stringifyComment, | ||
defaultKeyType: null, | ||
@@ -29,4 +27,23 @@ defaultStringType: 'PLAIN', | ||
verifyAliasOrder: true | ||
}, options) | ||
}); | ||
}, 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) { | ||
@@ -59,2 +76,4 @@ if (item.tag) { | ||
function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { | ||
if (!doc.directives) | ||
return ''; | ||
const props = []; | ||
@@ -66,15 +85,25 @@ const anchor = (Node.isScalar(node) || Node.isCollection(node)) && node.anchor; | ||
} | ||
if (node.tag) { | ||
props.push(doc.directives.tagString(node.tag)); | ||
} | ||
else if (!tagObj.default) { | ||
props.push(doc.directives.tagString(tagObj.tag)); | ||
} | ||
const 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) { | ||
var _a; | ||
if (Node.isPair(item)) | ||
return item.toString(ctx, onComment, onChompKeep); | ||
if (Node.isAlias(item)) | ||
return item.toString(ctx); | ||
if (Node.isAlias(item)) { | ||
if (ctx.doc.directives) | ||
return item.toString(ctx); | ||
if ((_a = ctx.resolvedAliases) === null || _a === void 0 ? void 0 : _a.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; | ||
@@ -81,0 +110,0 @@ const node = Node.isNode(item) |
import { Collection } from '../nodes/Collection.js'; | ||
import { StringifyContext } from './stringify.js'; | ||
declare type StringifyNode = { | ||
comment: boolean; | ||
str: string; | ||
}; | ||
interface StringifyCollectionOptions { | ||
blockItem(node: StringifyNode): string; | ||
blockItemPrefix: string; | ||
flowChars: { | ||
@@ -20,3 +16,3 @@ start: '{'; | ||
} | ||
export declare function stringifyCollection({ comment, flow, items }: Readonly<Collection>, ctx: StringifyContext, { blockItem, flowChars, itemIndent, onChompKeep, onComment }: StringifyCollectionOptions): string; | ||
export declare function stringifyCollection(collection: Readonly<Collection>, ctx: StringifyContext, options: StringifyCollectionOptions): string; | ||
export {}; |
@@ -8,31 +8,22 @@ 'use strict'; | ||
function stringifyCollection({ comment, flow, items }, ctx, { blockItem, flowChars, itemIndent, onChompKeep, onComment }) { | ||
const { indent, indentStep } = ctx; | ||
const inFlow = flow || ctx.inFlow; | ||
if (inFlow) | ||
itemIndent += indentStep; | ||
ctx = Object.assign({}, ctx, { indent: itemIndent, inFlow, type: null }); | ||
let singleLineOutput = true; | ||
function stringifyCollection(collection, ctx, options) { | ||
var _a; | ||
const flow = (_a = ctx.inFlow) !== null && _a !== void 0 ? _a : collection.flow; | ||
const stringify = flow ? stringifyFlowCollection : stringifyBlockCollection; | ||
return stringify(collection, ctx, options); | ||
} | ||
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { | ||
const { indent, options: { commentString } } = ctx; | ||
const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); | ||
let chompKeep = false; // flag for the preceding node's status | ||
const nodes = items.reduce((nodes, item, i) => { | ||
const lines = []; | ||
for (let i = 0; i < items.length; ++i) { | ||
const item = items[i]; | ||
let comment = null; | ||
if (Node.isNode(item)) { | ||
if (!chompKeep && item.spaceBefore) | ||
nodes.push({ comment: true, str: '' }); | ||
let cb = item.commentBefore; | ||
if (cb && chompKeep) | ||
cb = cb.replace(/^\n+/, ''); | ||
if (cb) { | ||
if (/^\n+$/.test(cb)) | ||
cb = cb.substring(1); | ||
// This match will always succeed on a non-empty string | ||
for (const line of cb.match(/^.*$/gm)) { | ||
const str = line === ' ' ? '#' : line ? `#${line}` : ''; | ||
nodes.push({ comment: true, str }); | ||
} | ||
} | ||
if (item.comment) { | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, item.commentBefore, chompKeep); | ||
if (item.comment) | ||
comment = item.comment; | ||
singleLineOutput = false; | ||
} | ||
} | ||
@@ -43,84 +34,123 @@ else if (Node.isPair(item)) { | ||
if (!chompKeep && ik.spaceBefore) | ||
nodes.push({ comment: true, str: '' }); | ||
let cb = ik.commentBefore; | ||
if (cb && chompKeep) | ||
cb = cb.replace(/^\n+/, ''); | ||
if (cb) { | ||
if (/^\n+$/.test(cb)) | ||
cb = cb.substring(1); | ||
// This match will always succeed on a non-empty string | ||
for (const line of cb.match(/^.*$/gm)) { | ||
const str = line === ' ' ? '#' : line ? `#${line}` : ''; | ||
nodes.push({ comment: true, str }); | ||
} | ||
} | ||
if (ik.comment) | ||
singleLineOutput = false; | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); | ||
} | ||
if (inFlow) { | ||
const iv = Node.isNode(item.value) ? item.value : null; | ||
if (iv) { | ||
if (iv.comment) | ||
comment = iv.comment; | ||
if (iv.comment || iv.commentBefore) | ||
singleLineOutput = false; | ||
} | ||
else if (item.value == null && ik && ik.comment) { | ||
comment = ik.comment; | ||
} | ||
} | ||
} | ||
chompKeep = false; | ||
let str = stringify.stringify(item, ctx, () => (comment = null), () => (chompKeep = true)); | ||
if (inFlow && i < items.length - 1) | ||
str += ','; | ||
str = stringifyComment.addComment(str, itemIndent, comment); | ||
if (chompKeep && (comment || inFlow)) | ||
let str = stringify.stringify(item, itemCtx, () => (comment = null), () => (chompKeep = true)); | ||
if (comment) | ||
str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); | ||
if (chompKeep && comment) | ||
chompKeep = false; | ||
nodes.push({ comment: false, str }); | ||
return nodes; | ||
}, []); | ||
lines.push(blockItemPrefix + str); | ||
} | ||
let str; | ||
if (nodes.length === 0) { | ||
if (lines.length === 0) { | ||
str = flowChars.start + flowChars.end; | ||
} | ||
else if (inFlow) { | ||
const { start, end } = flowChars; | ||
const strings = nodes.map(n => n.str); | ||
let singleLineLength = 2; | ||
for (const node of nodes) { | ||
if (node.comment || node.str.includes('\n')) { | ||
singleLineOutput = false; | ||
break; | ||
else { | ||
str = lines[0]; | ||
for (let i = 1; i < lines.length; ++i) { | ||
const line = lines[i]; | ||
str += line ? `\n${indent}${line}` : '\n'; | ||
} | ||
} | ||
if (comment) { | ||
str += '\n' + stringifyComment.indentComment(commentString(comment), indent); | ||
if (onComment) | ||
onComment(); | ||
} | ||
else if (chompKeep && onChompKeep) | ||
onChompKeep(); | ||
return str; | ||
} | ||
function stringifyFlowCollection({ comment, items }, ctx, { flowChars, itemIndent, onComment }) { | ||
const { indent, indentStep, options: { commentString } } = ctx; | ||
itemIndent += indentStep; | ||
const itemCtx = Object.assign({}, ctx, { | ||
indent: itemIndent, | ||
inFlow: true, | ||
type: null | ||
}); | ||
let reqNewline = false; | ||
let linesAtValue = 0; | ||
const lines = []; | ||
for (let i = 0; i < items.length; ++i) { | ||
const item = items[i]; | ||
let comment = null; | ||
if (Node.isNode(item)) { | ||
if (item.spaceBefore) | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, item.commentBefore, false); | ||
if (item.comment) | ||
comment = item.comment; | ||
} | ||
else if (Node.isPair(item)) { | ||
const ik = Node.isNode(item.key) ? item.key : null; | ||
if (ik) { | ||
if (ik.spaceBefore) | ||
lines.push(''); | ||
addCommentBefore(ctx, lines, ik.commentBefore, false); | ||
if (ik.comment) | ||
reqNewline = true; | ||
} | ||
singleLineLength += node.str.length + 2; | ||
const iv = Node.isNode(item.value) ? item.value : null; | ||
if (iv) { | ||
if (iv.comment) | ||
comment = iv.comment; | ||
if (iv.commentBefore) | ||
reqNewline = true; | ||
} | ||
else if (item.value == null && ik && ik.comment) { | ||
comment = ik.comment; | ||
} | ||
} | ||
if (!singleLineOutput || | ||
singleLineLength > Collection.Collection.maxFlowStringSingleLineLength) { | ||
if (comment) | ||
reqNewline = true; | ||
let str = stringify.stringify(item, itemCtx, () => (comment = null)); | ||
if (i < items.length - 1) | ||
str += ','; | ||
if (comment) | ||
str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); | ||
if (!reqNewline && (lines.length > linesAtValue || str.includes('\n'))) | ||
reqNewline = true; | ||
lines.push(str); | ||
linesAtValue = lines.length; | ||
} | ||
let str; | ||
const { start, end } = flowChars; | ||
if (lines.length === 0) { | ||
str = start + end; | ||
} | ||
else { | ||
if (!reqNewline) { | ||
const len = lines.reduce((sum, line) => sum + line.length + 2, 2); | ||
reqNewline = len > Collection.Collection.maxFlowStringSingleLineLength; | ||
} | ||
if (reqNewline) { | ||
str = start; | ||
for (const s of strings) { | ||
str += s ? `\n${indentStep}${indent}${s}` : '\n'; | ||
} | ||
for (const line of lines) | ||
str += line ? `\n${indentStep}${indent}${line}` : '\n'; | ||
str += `\n${indent}${end}`; | ||
} | ||
else { | ||
str = `${start} ${strings.join(' ')} ${end}`; | ||
str = `${start} ${lines.join(' ')} ${end}`; | ||
} | ||
} | ||
else { | ||
const strings = nodes.map(blockItem); | ||
str = strings.shift() || ''; | ||
for (const s of strings) | ||
str += s ? `\n${indent}${s}` : '\n'; | ||
} | ||
if (comment) { | ||
str += '\n' + stringifyComment.stringifyComment(comment, indent); | ||
str += stringifyComment.lineComment(str, commentString(comment), indent); | ||
if (onComment) | ||
onComment(); | ||
} | ||
else if (chompKeep && onChompKeep) | ||
onChompKeep(); | ||
return str; | ||
} | ||
function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { | ||
if (comment && chompKeep) | ||
comment = comment.replace(/^\n+/, ''); | ||
if (comment) { | ||
const ic = stringifyComment.indentComment(commentString(comment), indent); | ||
lines.push(ic.trimStart()); // Avoid double indent on first line | ||
} | ||
} | ||
exports.stringifyCollection = stringifyCollection; |
@@ -1,2 +0,10 @@ | ||
export declare const stringifyComment: (comment: string, indent: string) => string; | ||
export declare function addComment(str: string, indent: string, comment?: string | null): string; | ||
/** | ||
* Stringifies a comment. | ||
* | ||
* Empty comment lines are left empty, | ||
* lines consisting of a single space are replaced by `#`, | ||
* and all other lines are prefixed with a `#`. | ||
*/ | ||
export declare const stringifyComment: (str: string) => string; | ||
export declare function indentComment(comment: string, indent: string): string; | ||
export declare const lineComment: (str: string, indent: string, comment: string) => string; |
'use strict'; | ||
const stringifyComment = (comment, indent) => /^\n+$/.test(comment) | ||
? comment.substring(1) | ||
: comment.replace(/^(?!$)(?: $)?/gm, `${indent}#`); | ||
function addComment(str, indent, comment) { | ||
return !comment | ||
? str | ||
: comment.includes('\n') | ||
? `${str}\n` + stringifyComment(comment, indent) | ||
: str.endsWith(' ') | ||
? `${str}#${comment}` | ||
: `${str} #${comment}`; | ||
/** | ||
* Stringifies a comment. | ||
* | ||
* Empty comment lines are left empty, | ||
* lines consisting of a single space are replaced by `#`, | ||
* and all other lines are prefixed with a `#`. | ||
*/ | ||
const stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, '#'); | ||
function indentComment(comment, indent) { | ||
if (/^\n+$/.test(comment)) | ||
return comment.substring(1); | ||
return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; | ||
} | ||
const lineComment = (str, indent, comment) => comment.includes('\n') | ||
? '\n' + indentComment(comment, indent) | ||
: (str.endsWith(' ') ? '' : ' ') + comment; | ||
exports.addComment = addComment; | ||
exports.indentComment = indentComment; | ||
exports.lineComment = lineComment; | ||
exports.stringifyComment = stringifyComment; |
@@ -10,3 +10,3 @@ 'use strict'; | ||
let hasDirectives = options.directives === true; | ||
if (options.directives !== false) { | ||
if (options.directives !== false && doc.directives) { | ||
const dir = doc.directives.toString(doc); | ||
@@ -22,8 +22,10 @@ if (dir) { | ||
lines.push('---'); | ||
const ctx = stringify.createStringifyContext(doc, options); | ||
const { commentString } = ctx.options; | ||
if (doc.commentBefore) { | ||
if (lines.length !== 1) | ||
lines.unshift(''); | ||
lines.unshift(stringifyComment.stringifyComment(doc.commentBefore, '')); | ||
const cs = commentString(doc.commentBefore); | ||
lines.unshift(stringifyComment.indentComment(cs, '')); | ||
} | ||
const ctx = stringify.createStringifyContext(doc, options); | ||
let chompKeep = false; | ||
@@ -35,4 +37,6 @@ let contentComment = null; | ||
lines.push(''); | ||
if (doc.contents.commentBefore) | ||
lines.push(stringifyComment.stringifyComment(doc.contents.commentBefore, '')); | ||
if (doc.contents.commentBefore) { | ||
const cs = commentString(doc.contents.commentBefore); | ||
lines.push(stringifyComment.indentComment(cs, '')); | ||
} | ||
// top-level block scalars need to be indented if followed by a comment | ||
@@ -45,3 +49,3 @@ ctx.forceBlockIndent = !!doc.comment; | ||
if (contentComment) | ||
body = stringifyComment.addComment(body, '', contentComment); | ||
body += stringifyComment.lineComment(body, '', commentString(contentComment)); | ||
if ((body[0] === '|' || body[0] === '>') && | ||
@@ -65,3 +69,3 @@ lines[lines.length - 1] === '---') { | ||
lines.push(''); | ||
lines.push(stringifyComment.stringifyComment(dc, '')); | ||
lines.push(stringifyComment.indentComment(commentString(dc), '')); | ||
} | ||
@@ -68,0 +72,0 @@ return lines.join('\n') + '\n'; |
@@ -9,3 +9,3 @@ 'use strict'; | ||
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { | ||
const { allNullValues, doc, indent, indentStep, options: { indentSeq, simpleKeys } } = ctx; | ||
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; | ||
let keyComment = (Node.isNode(key) && key.comment) || null; | ||
@@ -49,13 +49,22 @@ if (simpleKeys) { | ||
else if ((allNullValues && !simpleKeys) || (value == null && explicitKey)) { | ||
if (keyCommentDone) | ||
keyComment = null; | ||
if (chompKeep && !keyComment && onChompKeep) | ||
str = `? ${str}`; | ||
if (keyComment && !keyCommentDone) { | ||
str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); | ||
} | ||
else if (chompKeep && onChompKeep) | ||
onChompKeep(); | ||
return stringifyComment.addComment(`? ${str}`, ctx.indent, keyComment); | ||
return str; | ||
} | ||
if (keyCommentDone) | ||
keyComment = null; | ||
str = explicitKey | ||
? `? ${stringifyComment.addComment(str, ctx.indent, keyComment)}\n${indent}:` | ||
: stringifyComment.addComment(`${str}:`, ctx.indent, keyComment); | ||
if (explicitKey) { | ||
if (keyComment) | ||
str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); | ||
str = `? ${str}\n${indent}:`; | ||
} | ||
else { | ||
str = `${str}:`; | ||
if (keyComment) | ||
str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); | ||
} | ||
let vcb = ''; | ||
@@ -66,4 +75,6 @@ let valueComment = null; | ||
vcb = '\n'; | ||
if (value.commentBefore) | ||
vcb += `\n${stringifyComment.stringifyComment(value.commentBefore, ctx.indent)}`; | ||
if (value.commentBefore) { | ||
const cs = commentString(value.commentBefore); | ||
vcb += `\n${stringifyComment.indentComment(cs, ctx.indent)}`; | ||
} | ||
valueComment = value.comment; | ||
@@ -102,16 +113,16 @@ } | ||
ws = ''; | ||
str += ws + valueStr; | ||
if (ctx.inFlow) { | ||
if (valueCommentDone && onComment) | ||
onComment(); | ||
return str + ws + valueStr; | ||
} | ||
else { | ||
if (valueCommentDone) | ||
valueComment = null; | ||
if (chompKeep && !valueComment && onChompKeep) | ||
onChompKeep(); | ||
return stringifyComment.addComment(str + ws + valueStr, ctx.indent, valueComment); | ||
else if (valueComment && !valueCommentDone) { | ||
str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); | ||
} | ||
else if (chompKeep && onChompKeep) { | ||
onChompKeep(); | ||
} | ||
return str; | ||
} | ||
exports.stringifyPair = stringifyPair; |
@@ -152,3 +152,3 @@ 'use strict'; | ||
function blockString({ comment, type, value }, ctx, onComment, onChompKeep) { | ||
const { lineWidth, blockQuote } = ctx.options; | ||
const { blockQuote, commentString, lineWidth } = ctx.options; | ||
// 1. Block can't end in whitespace unless the last line is non-empty. | ||
@@ -218,3 +218,3 @@ // 2. Strings consisting of only whitespace are best rendered explicitly. | ||
if (comment) { | ||
header += ' #' + comment.replace(/ ?[\r\n]+/g, ' '); | ||
header += ' ' + commentString(comment.replace(/ ?[\r\n]+/g, ' ')); | ||
if (onComment) | ||
@@ -236,3 +236,2 @@ onComment(); | ||
function plainString(item, ctx, onComment, onChompKeep) { | ||
var _a; | ||
const { type, value } = item; | ||
@@ -272,8 +271,6 @@ const { actualString, implicitKey, indent, inFlow } = ctx; | ||
if (actualString) { | ||
for (const tag of ctx.doc.schema.tags) { | ||
if (tag.default && | ||
tag.tag !== 'tag:yaml.org,2002:str' && | ||
((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(str))) | ||
return quotedString(value, ctx); | ||
} | ||
const test = (tag) => { var _a; return tag.default && tag.tag !== 'tag:yaml.org,2002:str' && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(str)); }; | ||
const { compat, tags } = ctx.doc.schema; | ||
if (tags.some(test) || (compat === null || compat === void 0 ? void 0 : compat.some(test))) | ||
return quotedString(value, ctx); | ||
} | ||
@@ -280,0 +277,0 @@ return implicitKey |
@@ -86,3 +86,4 @@ 'use strict'; | ||
if (Node.isMap(node)) { | ||
events.push(`+MAP${props}`); | ||
const ev = node.flow ? '+MAP {}' : '+MAP'; | ||
events.push(`${ev}${props}`); | ||
node.items.forEach(({ key, value }) => { | ||
@@ -95,3 +96,4 @@ addEvents(events, doc, errPos, key); | ||
else if (Node.isSeq(node)) { | ||
events.push(`+SEQ${props}`); | ||
const ev = node.flow ? '+SEQ []' : '+SEQ'; | ||
events.push(`${ev}${props}`); | ||
node.items.forEach(item => { | ||
@@ -98,0 +100,0 @@ addEvents(events, doc, errPos, item); |
{ | ||
"name": "yaml", | ||
"version": "2.0.0-9", | ||
"version": "2.0.0-10", | ||
"license": "ISC", | ||
@@ -48,5 +48,6 @@ "author": "Eemeli Aro <eemeli@gmail.com>", | ||
"test": "jest --config config/jest.config.js", | ||
"test:all": "npm test && npm run test:types && npm run test:dist && npm run test:dist:types", | ||
"test:browsers": "cd playground && npm test", | ||
"test:dist": "npm run build:node && jest --config config/jest.config.js", | ||
"test:dist:types": "tsc --allowJs --moduleResolution node --noEmit --target es2017 dist/index.js", | ||
"test:dist:types": "tsc --allowJs --moduleResolution node --noEmit --target es5 dist/index.js", | ||
"test:types": "tsc --noEmit", | ||
@@ -77,7 +78,7 @@ "docs:install": "cd docs-slate && bundle install", | ||
"@types/node": "^16.9.1", | ||
"@typescript-eslint/eslint-plugin": "^4.15.2", | ||
"@typescript-eslint/parser": "^4.15.2", | ||
"@typescript-eslint/eslint-plugin": "^5.3.1", | ||
"@typescript-eslint/parser": "^5.3.1", | ||
"babel-jest": "^27.0.1", | ||
"cross-env": "^7.0.3", | ||
"eslint": "^7.20.0", | ||
"eslint": "^8.2.0", | ||
"eslint-config-prettier": "^8.1.0", | ||
@@ -84,0 +85,0 @@ "fast-check": "^2.12.0", |
633001
228
16856