| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 | 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    }}
 |