| const util = require('./util') | 
|   | 
| module.exports = function stringify (value, replacer, space) { | 
|     const stack = [] | 
|     let indent = '' | 
|     let propertyList | 
|     let replacerFunc | 
|     let gap = '' | 
|     let quote | 
|   | 
|     if ( | 
|         replacer != null && | 
|         typeof replacer === 'object' && | 
|         !Array.isArray(replacer) | 
|     ) { | 
|         space = replacer.space | 
|         quote = replacer.quote | 
|         replacer = replacer.replacer | 
|     } | 
|   | 
|     if (typeof replacer === 'function') { | 
|         replacerFunc = replacer | 
|     } else if (Array.isArray(replacer)) { | 
|         propertyList = [] | 
|         for (const v of replacer) { | 
|             let item | 
|   | 
|             if (typeof v === 'string') { | 
|                 item = v | 
|             } else if ( | 
|                 typeof v === 'number' || | 
|                 v instanceof String || | 
|                 v instanceof Number | 
|             ) { | 
|                 item = String(v) | 
|             } | 
|   | 
|             if (item !== undefined && propertyList.indexOf(item) < 0) { | 
|                 propertyList.push(item) | 
|             } | 
|         } | 
|     } | 
|   | 
|     if (space instanceof Number) { | 
|         space = Number(space) | 
|     } else if (space instanceof String) { | 
|         space = String(space) | 
|     } | 
|   | 
|     if (typeof space === 'number') { | 
|         if (space > 0) { | 
|             space = Math.min(10, Math.floor(space)) | 
|             gap = '          '.substr(0, space) | 
|         } | 
|     } else if (typeof space === 'string') { | 
|         gap = space.substr(0, 10) | 
|     } | 
|   | 
|     return serializeProperty('', {'': value}) | 
|   | 
|     function serializeProperty (key, holder) { | 
|         let value = holder[key] | 
|         if (value != null) { | 
|             if (typeof value.toJSON5 === 'function') { | 
|                 value = value.toJSON5(key) | 
|             } else if (typeof value.toJSON === 'function') { | 
|                 value = value.toJSON(key) | 
|             } | 
|         } | 
|   | 
|         if (replacerFunc) { | 
|             value = replacerFunc.call(holder, key, value) | 
|         } | 
|   | 
|         if (value instanceof Number) { | 
|             value = Number(value) | 
|         } else if (value instanceof String) { | 
|             value = String(value) | 
|         } else if (value instanceof Boolean) { | 
|             value = value.valueOf() | 
|         } | 
|   | 
|         switch (value) { | 
|         case null: return 'null' | 
|         case true: return 'true' | 
|         case false: return 'false' | 
|         } | 
|   | 
|         if (typeof value === 'string') { | 
|             return quoteString(value, false) | 
|         } | 
|   | 
|         if (typeof value === 'number') { | 
|             return String(value) | 
|         } | 
|   | 
|         if (typeof value === 'object') { | 
|             return Array.isArray(value) ? serializeArray(value) : serializeObject(value) | 
|         } | 
|   | 
|         return undefined | 
|     } | 
|   | 
|     function quoteString (value) { | 
|         const quotes = { | 
|             "'": 0.1, | 
|             '"': 0.2, | 
|         } | 
|   | 
|         const replacements = { | 
|             "'": "\\'", | 
|             '"': '\\"', | 
|             '\\': '\\\\', | 
|             '\b': '\\b', | 
|             '\f': '\\f', | 
|             '\n': '\\n', | 
|             '\r': '\\r', | 
|             '\t': '\\t', | 
|             '\v': '\\v', | 
|             '\0': '\\0', | 
|             '\u2028': '\\u2028', | 
|             '\u2029': '\\u2029', | 
|         } | 
|   | 
|         let product = '' | 
|   | 
|         for (let i = 0; i < value.length; i++) { | 
|             const c = value[i] | 
|             switch (c) { | 
|             case "'": | 
|             case '"': | 
|                 quotes[c]++ | 
|                 product += c | 
|                 continue | 
|   | 
|             case '\0': | 
|                 if (util.isDigit(value[i + 1])) { | 
|                     product += '\\x00' | 
|                     continue | 
|                 } | 
|             } | 
|   | 
|             if (replacements[c]) { | 
|                 product += replacements[c] | 
|                 continue | 
|             } | 
|   | 
|             if (c < ' ') { | 
|                 let hexString = c.charCodeAt(0).toString(16) | 
|                 product += '\\x' + ('00' + hexString).substring(hexString.length) | 
|                 continue | 
|             } | 
|   | 
|             product += c | 
|         } | 
|   | 
|         const quoteChar = quote || Object.keys(quotes).reduce((a, b) => (quotes[a] < quotes[b]) ? a : b) | 
|   | 
|         product = product.replace(new RegExp(quoteChar, 'g'), replacements[quoteChar]) | 
|   | 
|         return quoteChar + product + quoteChar | 
|     } | 
|   | 
|     function serializeObject (value) { | 
|         if (stack.indexOf(value) >= 0) { | 
|             throw TypeError('Converting circular structure to JSON5') | 
|         } | 
|   | 
|         stack.push(value) | 
|   | 
|         let stepback = indent | 
|         indent = indent + gap | 
|   | 
|         let keys = propertyList || Object.keys(value) | 
|         let partial = [] | 
|         for (const key of keys) { | 
|             const propertyString = serializeProperty(key, value) | 
|             if (propertyString !== undefined) { | 
|                 let member = serializeKey(key) + ':' | 
|                 if (gap !== '') { | 
|                     member += ' ' | 
|                 } | 
|                 member += propertyString | 
|                 partial.push(member) | 
|             } | 
|         } | 
|   | 
|         let final | 
|         if (partial.length === 0) { | 
|             final = '{}' | 
|         } else { | 
|             let properties | 
|             if (gap === '') { | 
|                 properties = partial.join(',') | 
|                 final = '{' + properties + '}' | 
|             } else { | 
|                 let separator = ',\n' + indent | 
|                 properties = partial.join(separator) | 
|                 final = '{\n' + indent + properties + ',\n' + stepback + '}' | 
|             } | 
|         } | 
|   | 
|         stack.pop() | 
|         indent = stepback | 
|         return final | 
|     } | 
|   | 
|     function serializeKey (key) { | 
|         if (key.length === 0) { | 
|             return quoteString(key, true) | 
|         } | 
|   | 
|         const firstChar = String.fromCodePoint(key.codePointAt(0)) | 
|         if (!util.isIdStartChar(firstChar)) { | 
|             return quoteString(key, true) | 
|         } | 
|   | 
|         for (let i = firstChar.length; i < key.length; i++) { | 
|             if (!util.isIdContinueChar(String.fromCodePoint(key.codePointAt(i)))) { | 
|                 return quoteString(key, true) | 
|             } | 
|         } | 
|   | 
|         return key | 
|     } | 
|   | 
|     function serializeArray (value) { | 
|         if (stack.indexOf(value) >= 0) { | 
|             throw TypeError('Converting circular structure to JSON5') | 
|         } | 
|   | 
|         stack.push(value) | 
|   | 
|         let stepback = indent | 
|         indent = indent + gap | 
|   | 
|         let partial = [] | 
|         for (let i = 0; i < value.length; i++) { | 
|             const propertyString = serializeProperty(String(i), value) | 
|             partial.push((propertyString !== undefined) ? propertyString : 'null') | 
|         } | 
|   | 
|         let final | 
|         if (partial.length === 0) { | 
|             final = '[]' | 
|         } else { | 
|             if (gap === '') { | 
|                 let properties = partial.join(',') | 
|                 final = '[' + properties + ']' | 
|             } else { | 
|                 let separator = ',\n' + indent | 
|                 let properties = partial.join(separator) | 
|                 final = '[\n' + indent + properties + ',\n' + stepback + ']' | 
|             } | 
|         } | 
|   | 
|         stack.pop() | 
|         indent = stepback | 
|         return final | 
|     } | 
| } |