stringify.js
function stringify (input, {
  commentPrefix = '# ',
  defaultKey = '',
  indent = '    ',
  keySep = ' = ',
  latin1 = true,
  lineWidth = 80,
  newline = '\n',
  pathSep = '.'
} = {}) {
  if (!input) return ''
  if (!Array.isArray(input)) input = toLines(input, pathSep, defaultKey)
  const foldLine = getFold({ indent, latin1, lineWidth, newline: '\\' + newline })
  const foldComment = getFold({ indent: commentPrefix, latin1, lineWidth, newline })
  return input
    .map(line => {
      switch (true) {
        case !line:
          return ''
        case Array.isArray(line):
          const key = escapeKey(line[0])
          const value = escapeValue(line[1])
          return foldLine(key + keySep + value)
        default:
          const cc = String(line).replace(/^\s*([#!][ \t\f]*)?/g, commentPrefix)
          return foldComment(cc)
      }
    })
    .join(newline)
}
function stringify (input, {
  commentPrefix = '# ',
  defaultKey = '',
  indent = '    ',
  keySep = ' = ',
  latin1 = true,
  lineWidth = 80,
  newline = '\n',
  pathSep = '.'
} = {}) {
  if (!input) return ''
  if (!Array.isArray(input)) input = toLines(input, pathSep, defaultKey)
  const foldLine = getFold({ indent, latin1, lineWidth, newline: '\\' + newline })
  const foldComment = getFold({ indent: commentPrefix, latin1, lineWidth, newline })
  return input
    .map(line => {
      switch (true) {
        case !line:
          return ''
        case Array.isArray(line):
          const key = escapeKey(line[0])
          const value = escapeValue(line[1])
          return foldLine(key + keySep + value)
        default:
          const cc = String(line).replace(/^\s*([#!][ \t\f]*)?/g, commentPrefix)
          return foldComment(cc)
      }
    })
    .join(newline)
}
function stringify ( input , { commentPrefix = '# ' , defaultKey = '' , indent = '    ' , keySep = ' = ' , latin1 = true , lineWidth = 80 , newline = '\n' , pathSep = '.' } = { } ) { if ( ! input ) return '' if ( ! Array . isArray ( input ) ) input = toLines ( input , pathSep , defaultKey ) const foldLine = getFold ( { indent , latin1 , lineWidth , newline : '\\' + newline } ) const foldComment = getFold ( { indent : commentPrefix , latin1 , lineWidth , newline } ) return input . map ( line => { switch ( true ) { case ! line : return '' case Array . isArray ( line ) : const key = escapeKey ( line [ 0 ] ) const value = escapeValue ( line [ 1 ] ) return foldLine ( key + keySep + value ) default : const cc = String ( line ) . replace ( / ^\s*([#!][ \t\f]*)? / g , commentPrefix ) return foldComment ( cc ) } } ) . join ( newline ) }
Stringifies a hierarchical object or an array of lines to .properties format

If the input is a hierarchical object, keys will consist of the path parts
joined by '.' characters. With array input, string values represent blank or
comment lines and string arrays are [key, value] pairs. The characters \, \n
and \r will be appropriately escaped. If the `latin1` option is not set to
false, all non-Latin-1 characters will also be `\u` escaped.

Output styling is controlled by the second options parameter; by default a
spaced '=' separates the key from the value, '\n' is the newline separator,
lines are folded at 80 characters, with subsequent lines indented by four
spaces, and comment lines are prefixed with a '#'. '' as a key value is
considered the default, and set as the value of a key corresponding to its
parent object's path.

@param {Object | Array<string | string[]>} input
@param {Object} [options={}]
@param {string} [options.commentPrefix='# ']
@param {string} [options.defaultKey='']
@param {string} [options.indent='    ']
@param {string} [options.keySep=' = ']
@param {boolean} [options.latin1=true]
@param {number} [options.lineWidth=80]
@param {string} [options.newline='\n']
@param {string} [options.pathSep='.']
Stringifies a hierarchical object or an array of lines to . properties format