You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					262 lines
				
				6.8 KiB
			
		
		
			
		
	
	
					262 lines
				
				6.8 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								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
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |