| 
 | 1 | +/*  | 
 | 2 | +Copyright (c) 2014, Yahoo! Inc. All rights reserved.  | 
 | 3 | +Copyrights licensed under the New BSD License.  | 
 | 4 | +See the accompanying LICENSE file for terms.  | 
 | 5 | +*/  | 
 | 6 | + | 
 | 7 | +'use strict';  | 
 | 8 | + | 
 | 9 | +var util = require('util');  | 
 | 10 | + | 
 | 11 | +module.exports = serialize;  | 
 | 12 | + | 
 | 13 | +var IS_NATIVE_CODE_REGEX = /\{\s*\[native code\]\s*\}/g,  | 
 | 14 | +    PLACE_HOLDER_REGEX   = /"@__(FUNCTION|REGEXP)_(\d+)__@"/g,  | 
 | 15 | +    UNSAFE_CHARS_REGEX   = /[<>\/\u2028\u2029]/g;  | 
 | 16 | + | 
 | 17 | +// Mapping of unsafe HTML and invalid JavaScript line terminator chars to their  | 
 | 18 | +// Unicode char counterparts which are safe to use in JavaScript strings.  | 
 | 19 | +var UNICODE_CHARS = {  | 
 | 20 | +    '<'     : '\\u003C',  | 
 | 21 | +    '>'     : '\\u003E',  | 
 | 22 | +    '/'     : '\\u002F',  | 
 | 23 | +    '\u2028': '\\u2028',  | 
 | 24 | +    '\u2029': '\\u2029'  | 
 | 25 | +};  | 
 | 26 | + | 
 | 27 | +function serialize(obj) {  | 
 | 28 | +    var functions = [],  | 
 | 29 | +        regexps   = [],  | 
 | 30 | +        str;  | 
 | 31 | + | 
 | 32 | +    // Creates a JSON string representation of the object and uses placeholders  | 
 | 33 | +    // for functions and regexps (identified by index) which are later  | 
 | 34 | +    // replaced.  | 
 | 35 | +    str = JSON.stringify(obj, function (key, value) {  | 
 | 36 | +        if (typeof value === 'function') {  | 
 | 37 | +            return '@__FUNCTION_' + (functions.push(value) - 1) + '__@';  | 
 | 38 | +        }  | 
 | 39 | + | 
 | 40 | +        if (util.isRegExp(value)) {  | 
 | 41 | +            return '@__REGEXP_' + (regexps.push(value) - 1) + '__@';  | 
 | 42 | +        }  | 
 | 43 | + | 
 | 44 | +        return value;  | 
 | 45 | +    });  | 
 | 46 | + | 
 | 47 | +    // Protects against `JSON.stringify()` returning `undefined`, by serializing  | 
 | 48 | +    // to the literal string: "undefined".  | 
 | 49 | +    if (typeof str !== 'string') {  | 
 | 50 | +        return String(str);  | 
 | 51 | +    }  | 
 | 52 | + | 
 | 53 | +    // Replace unsafe HTML and invalid JavaScript line terminator chars with  | 
 | 54 | +    // their safe Unicode char counterpart. This _must_ happen before the  | 
 | 55 | +    // regexps and functions are serialized and added back to the string.  | 
 | 56 | +    str = str.replace(UNSAFE_CHARS_REGEX, function (unsafeChar) {  | 
 | 57 | +        return UNICODE_CHARS[unsafeChar];  | 
 | 58 | +    });  | 
 | 59 | + | 
 | 60 | +    if (!(functions.length || regexps.length)) {  | 
 | 61 | +        return str;  | 
 | 62 | +    }  | 
 | 63 | + | 
 | 64 | +    // Replaces all occurrences of function and regexp placeholders in the JSON  | 
 | 65 | +    // string with their string representations. If the original value can not  | 
 | 66 | +    // be found, then `undefined` is used.  | 
 | 67 | +    return str.replace(PLACE_HOLDER_REGEX, function (match, type, index) {  | 
 | 68 | +        if (type === 'REGEXP') {  | 
 | 69 | +            return regexps[index].toString();  | 
 | 70 | +        }  | 
 | 71 | + | 
 | 72 | +        var fn           = functions[index],  | 
 | 73 | +            serializedFn = fn.toString();  | 
 | 74 | + | 
 | 75 | +        if (IS_NATIVE_CODE_REGEX.test(serializedFn)) {  | 
 | 76 | +            throw new TypeError('Serializing native function: ' + fn.name);  | 
 | 77 | +        }  | 
 | 78 | + | 
 | 79 | +        return serializedFn;  | 
 | 80 | +    });  | 
 | 81 | +}  | 
0 commit comments