/** * Copyright (c) 2016, Rebecca Turner * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * We have made some modifications to this file to support the bruno-toml * You can search for "modified for bruno-toml" to see the changes */ 'use strict' module.exports = stringify module.exports.value = stringifyInline function stringify (obj) { if (obj === null) throw typeError('null') if (obj === void (0)) throw typeError('undefined') if (typeof obj !== 'object') throw typeError(typeof obj) if (typeof obj.toJSON === 'function') obj = obj.toJSON() if (obj == null) return null const type = tomlType(obj) if (type !== 'table') throw typeError(type) return stringifyObject('', '', obj) } function typeError (type) { return new Error('Can only stringify objects, not ' + type) } function getInlineKeys (obj) { return Object.keys(obj).filter(key => isInline(obj[key])) } function getComplexKeys (obj) { return Object.keys(obj).filter(key => !isInline(obj[key])) } function toJSON (obj) { let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, '__proto__') ? {['__proto__']: undefined} : {} for (let prop of Object.keys(obj)) { if (obj[prop] && typeof obj[prop].toJSON === 'function' && !('toISOString' in obj[prop])) { nobj[prop] = obj[prop].toJSON() } else { nobj[prop] = obj[prop] } } return nobj } function stringifyObject (prefix, indent, obj) { obj = toJSON(obj) let inlineKeys let complexKeys inlineKeys = getInlineKeys(obj) complexKeys = getComplexKeys(obj) const result = [] const inlineIndent = indent || '' inlineKeys.forEach(key => { var type = tomlType(obj[key]) if (type !== 'undefined' && type !== 'null') { result.push(inlineIndent + stringifyKey(key) + ' = ' + stringifyAnyInline(obj[key], true)) } }) if (result.length > 0) result.push('') const complexIndent = prefix && inlineKeys.length > 0 ? indent + ' ' : '' complexKeys.forEach(key => { result.push(stringifyComplex(prefix, complexIndent, key, obj[key])) }) return result.join('\n') } function isInline (value) { switch (tomlType(value)) { case 'undefined': case 'null': case 'integer': case 'nan': case 'float': case 'boolean': case 'string': case 'datetime': return true case 'array': return value.length === 0 || tomlType(value[0]) !== 'table' case 'table': return Object.keys(value).length === 0 /* istanbul ignore next */ default: return false } } function tomlType (value) { if (value === undefined) { return 'undefined' } else if (value === null) { return 'null' } else if (typeof value === 'bigint' || (Number.isInteger(value) && !Object.is(value, -0))) { return 'integer' } else if (typeof value === 'number') { return 'float' } else if (typeof value === 'boolean') { return 'boolean' } else if (typeof value === 'string') { return 'string' } else if ('toISOString' in value) { return isNaN(value) ? 'undefined' : 'datetime' } else if (Array.isArray(value)) { return 'array' } else { return 'table' } } function stringifyKey (key) { const keyStr = String(key) if (/^[-A-Za-z0-9_]+$/.test(keyStr)) { return keyStr } else { return stringifyBasicString(keyStr) } } function stringifyBasicString (str) { // original // return '"' + escapeString(str).replace(/"/g, '\\"') + '"' // modified for bruno-toml return "'" + escapeString(str).replace(/'/g, "\\'") + "'" } function stringifyLiteralString (str) { return "'" + str + "'" } function numpad (num, str) { while (str.length < num) str = '0' + str return str } function escapeString (str) { return str.replace(/\\/g, '\\\\') .replace(/[\b]/g, '\\b') .replace(/\t/g, '\\t') .replace(/\n/g, '\\n') .replace(/\f/g, '\\f') .replace(/\r/g, '\\r') .replace(/([\u0000-\u001f\u007f])/, c => '\\u' + numpad(4, c.codePointAt(0).toString(16))) } function stringifyMultilineString (str) { // original // let escaped = str.split(/\n/).map(str => { // return escapeString(str).replace(/"(?="")/g, '\\"') // }).join('\n') // if (escaped.slice(-1) === '"') escaped += '\\\n' // return '"""\n' + escaped + '"""' // modified for bruno-toml let escaped = str.split(/\n/).map(str => { return escapeString(str).replace(/'(?='')/g, "\\'") }).join('\n') if (escaped.slice(-1) === "'") escaped += '\\\n' return "'''\n" + escaped + "\n'''" } function stringifyAnyInline (value, multilineOk) { let type = tomlType(value) if (type === 'string') { if (multilineOk && /\n/.test(value)) { type = 'string-multiline' } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) { type = 'string-literal' } } return stringifyInline(value, type) } function stringifyInline (value, type) { if (!type) type = tomlType(value) switch (type) { case 'string-multiline': return stringifyMultilineString(value) case 'string': return stringifyBasicString(value) case 'string-literal': return stringifyLiteralString(value) case 'integer': return stringifyInteger(value) case 'float': return stringifyFloat(value) case 'boolean': return stringifyBoolean(value) case 'datetime': return stringifyDatetime(value) case 'array': return stringifyInlineArray(value.filter(_ => tomlType(_) !== 'null' && tomlType(_) !== 'undefined' && tomlType(_) !== 'nan')) case 'table': return stringifyInlineTable(value) /* istanbul ignore next */ default: throw typeError(type) } } function stringifyInteger (value) { return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, '_') } function stringifyFloat (value) { if (value === Infinity) { return 'inf' } else if (value === -Infinity) { return '-inf' } else if (Object.is(value, NaN)) { return 'nan' } else if (Object.is(value, -0)) { return '-0.0' } const [int, dec] = String(value).split('.') return stringifyInteger(int) + '.' + dec } function stringifyBoolean (value) { return String(value) } function stringifyDatetime (value) { return value.toISOString() } function stringifyInlineArray (values) { values = toJSON(values) let result = '[' const stringified = values.map(_ => stringifyInline(_)) if (stringified.join(', ').length > 60 || /\n/.test(stringified)) { result += '\n ' + stringified.join(',\n ') + '\n' } else { result += ' ' + stringified.join(', ') + (stringified.length > 0 ? ' ' : '') } return result + ']' } function stringifyInlineTable (value) { value = toJSON(value) const result = [] Object.keys(value).forEach(key => { result.push(stringifyKey(key) + ' = ' + stringifyAnyInline(value[key], false)) }) return '{ ' + result.join(', ') + (result.length > 0 ? ' ' : '') + '}' } function stringifyComplex (prefix, indent, key, value) { const valueType = tomlType(value) /* istanbul ignore else */ if (valueType === 'array') { return stringifyArrayOfTables(prefix, indent, key, value) } else if (valueType === 'table') { return stringifyComplexTable(prefix, indent, key, value) } else { throw typeError(valueType) } } function stringifyArrayOfTables (prefix, indent, key, values) { values = toJSON(values) const firstValueType = tomlType(values[0]) /* istanbul ignore if */ if (firstValueType !== 'table') throw typeError(firstValueType) const fullKey = prefix + stringifyKey(key) let result = '' values.forEach(table => { if (result.length > 0) result += '\n' result += indent + '[[' + fullKey + ']]\n' result += stringifyObject(fullKey + '.', indent, table) }) return result } function stringifyComplexTable (prefix, indent, key, value) { const fullKey = prefix + stringifyKey(key) let result = '' if (getInlineKeys(value).length > 0) { result += indent + '[' + fullKey + ']\n' } return result + stringifyObject(fullKey + '.', indent, value) }