import { addCommentBefore } from './addComment'; import { Type } from './constants'; import foldFlowLines, { FOLD_BLOCK, FOLD_FLOW, FOLD_QUOTED } from './foldFlowLines'; import { strOptions } from './tags/options'; var getFoldOptions = function getFoldOptions(_ref) { var indentAtStart = _ref.indentAtStart; return indentAtStart ? Object.assign({ indentAtStart: indentAtStart }, strOptions.fold) : strOptions.fold; }; export function stringifyNumber(_ref2) { var format = _ref2.format, minFractionDigits = _ref2.minFractionDigits, tag = _ref2.tag, value = _ref2.value; if (!isFinite(value)) return isNaN(value) ? '.nan' : value < 0 ? '-.inf' : '.inf'; var n = JSON.stringify(value); if (!format && minFractionDigits && (!tag || tag === 'tag:yaml.org,2002:float') && /^\d/.test(n)) { var i = n.indexOf('.'); if (i < 0) { i = n.length; n += '.'; } var d = minFractionDigits - (n.length - i - 1); while (d-- > 0) { n += '0'; } } return n; } function lineLengthOverLimit(str, limit) { var strLen = str.length; if (strLen <= limit) return false; for (var i = 0, start = 0; i < strLen; ++i) { if (str[i] === '\n') { if (i - start > limit) return true; start = i + 1; if (strLen - start <= limit) return false; } } return true; } function doubleQuotedString(value, ctx) { var implicitKey = ctx.implicitKey, indent = ctx.indent; var _strOptions$doubleQuo = strOptions.doubleQuoted, jsonEncoding = _strOptions$doubleQuo.jsonEncoding, minMultiLineLength = _strOptions$doubleQuo.minMultiLineLength; var json = JSON.stringify(value); if (jsonEncoding) return json; var str = ''; var start = 0; for (var i = 0, ch = json[i]; ch; ch = json[++i]) { if (ch === ' ' && json[i + 1] === '\\' && json[i + 2] === 'n') { // space before newline needs to be escaped to not be folded str += json.slice(start, i) + '\\ '; i += 1; start = i; ch = '\\'; } if (ch === '\\') switch (json[i + 1]) { case 'u': { str += json.slice(start, i); var code = json.substr(i + 2, 4); switch (code) { case '0000': str += '\\0'; break; case '0007': str += '\\a'; break; case '000b': str += '\\v'; break; case '001b': str += '\\e'; break; case '0085': str += '\\N'; break; case '00a0': str += '\\_'; break; case '2028': str += '\\L'; break; case '2029': str += '\\P'; break; default: if (code.substr(0, 2) === '00') str += '\\x' + code.substr(2);else str += json.substr(i, 6); } i += 5; start = i + 1; } break; case 'n': if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { i += 1; } else { // folding will eat first newline str += json.slice(start, i) + '\n\n'; while (json[i + 2] === '\\' && json[i + 3] === 'n' && json[i + 4] !== '"') { str += '\n'; i += 2; } str += indent; // space after newline needs to be escaped to not be folded if (json[i + 2] === ' ') str += '\\'; i += 1; start = i + 1; } break; default: i += 1; } } str = start ? str + json.slice(start) : json; return implicitKey ? str : foldFlowLines(str, indent, FOLD_QUOTED, getFoldOptions(ctx)); } function singleQuotedString(value, ctx) { var indent = ctx.indent, implicitKey = ctx.implicitKey; if (implicitKey) { if (/\n/.test(value)) return doubleQuotedString(value, ctx); } else { // single quoted string can't have leading or trailing whitespace around newline if (/[ \t]\n|\n[ \t]/.test(value)) return doubleQuotedString(value, ctx); } var res = "'" + value.replace(/'/g, "''").replace(/\n+/g, "$&\n".concat(indent)) + "'"; return implicitKey ? res : foldFlowLines(res, indent, FOLD_FLOW, getFoldOptions(ctx)); } function blockString(_ref3, ctx, onComment, onChompKeep) { var comment = _ref3.comment, type = _ref3.type, value = _ref3.value; // 1. Block can't end in whitespace unless the last line is non-empty. // 2. Strings consisting of only whitespace are best rendered explicitly. if (/\n[\t ]+$/.test(value) || /^\s*$/.test(value)) { return doubleQuotedString(value, ctx); } var indent = ctx.indent || (ctx.forceBlockIndent ? ' ' : ''); var indentSize = indent ? '2' : '1'; // root is at -1 var literal = type === Type.BLOCK_FOLDED ? false : type === Type.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, strOptions.fold.lineWidth - indent.length); var header = literal ? '|' : '>'; if (!value) return header + '\n'; var wsStart = ''; var wsEnd = ''; value = value.replace(/[\n\t ]*$/, function (ws) { var n = ws.indexOf('\n'); if (n === -1) { header += '-'; // strip } else if (value === ws || n !== ws.length - 1) { header += '+'; // keep if (onChompKeep) onChompKeep(); } wsEnd = ws.replace(/\n$/, ''); return ''; }).replace(/^[\n ]*/, function (ws) { if (ws.indexOf(' ') !== -1) header += indentSize; var m = ws.match(/ +$/); if (m) { wsStart = ws.slice(0, -m[0].length); return m[0]; } else { wsStart = ws; return ''; } }); if (wsEnd) wsEnd = wsEnd.replace(/\n+(?!\n|$)/g, "$&".concat(indent)); if (wsStart) wsStart = wsStart.replace(/\n+/g, "$&".concat(indent)); if (comment) { header += ' #' + comment.replace(/ ?[\r\n]+/g, ' '); if (onComment) onComment(); } if (!value) return "".concat(header).concat(indentSize, "\n").concat(indent).concat(wsEnd); if (literal) { value = value.replace(/\n+/g, "$&".concat(indent)); return "".concat(header, "\n").concat(indent).concat(wsStart).concat(value).concat(wsEnd); } value = value.replace(/\n+/g, '\n$&').replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, '$1$2') // more-indented lines aren't folded // ^ ind.line ^ empty ^ capture next empty lines only at end of indent .replace(/\n+/g, "$&".concat(indent)); var body = foldFlowLines("".concat(wsStart).concat(value).concat(wsEnd), indent, FOLD_BLOCK, strOptions.fold); return "".concat(header, "\n").concat(indent).concat(body); } function plainString(item, ctx, onComment, onChompKeep) { var comment = item.comment, type = item.type, value = item.value; var actualString = ctx.actualString, implicitKey = ctx.implicitKey, indent = ctx.indent, inFlow = ctx.inFlow, tags = ctx.tags; if (implicitKey && /[\n[\]{},]/.test(value) || inFlow && /[[\]{},]/.test(value)) { return doubleQuotedString(value, ctx); } if (!value || /^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { // not allowed: // - empty string, '-' or '?' // - start with an indicator character (except [?:-]) or /[?-] / // - '\n ', ': ' or ' \n' anywhere // - '#' not preceded by a non-space char // - end with ' ' or ':' return implicitKey || inFlow || value.indexOf('\n') === -1 ? value.indexOf('"') !== -1 && value.indexOf("'") === -1 ? singleQuotedString(value, ctx) : doubleQuotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); } if (!implicitKey && !inFlow && type !== Type.PLAIN && value.indexOf('\n') !== -1) { // Where allowed & type not set explicitly, prefer block style for multiline strings return blockString(item, ctx, onComment, onChompKeep); } var str = value.replace(/\n+/g, "$&\n".concat(indent)); // Verify that output will be parsed as a string, as e.g. plain numbers and // booleans get parsed with those types in v1.2 (e.g. '42', 'true' & '0.9e-3'), // and others in v1.1. if (actualString && typeof tags.resolveScalar(str).value !== 'string') { return doubleQuotedString(value, ctx); } var body = implicitKey ? str : foldFlowLines(str, indent, FOLD_FLOW, getFoldOptions(ctx)); if (comment && !inFlow && (body.indexOf('\n') !== -1 || comment.indexOf('\n') !== -1)) { if (onComment) onComment(); return addCommentBefore(body, indent, comment); } return body; } export function stringifyString(item, ctx, onComment, onChompKeep) { var defaultType = strOptions.defaultType; var implicitKey = ctx.implicitKey, inFlow = ctx.inFlow; var _item = item, type = _item.type, value = _item.value; if (typeof value !== 'string') { value = String(value); item = Object.assign({}, item, { value: value }); } var _stringify = function _stringify(_type) { switch (_type) { case Type.BLOCK_FOLDED: case Type.BLOCK_LITERAL: return blockString(item, ctx, onComment, onChompKeep); case Type.QUOTE_DOUBLE: return doubleQuotedString(value, ctx); case Type.QUOTE_SINGLE: return singleQuotedString(value, ctx); case Type.PLAIN: return plainString(item, ctx, onComment, onChompKeep); default: return null; } }; if (type !== Type.QUOTE_DOUBLE && /[\x00-\x08\x0b-\x1f\x7f-\x9f]/.test(value)) { // force double quotes on control characters type = Type.QUOTE_DOUBLE; } else if ((implicitKey || inFlow) && (type === Type.BLOCK_FOLDED || type === Type.BLOCK_LITERAL)) { // should not happen; blocks are not valid inside flow containers type = Type.QUOTE_DOUBLE; } var res = _stringify(type); if (res === null) { res = _stringify(defaultType); if (res === null) throw new Error("Unsupported default string type ".concat(defaultType)); } return res; }