Blame view

node_modules/serialize-javascript/index.js 3.84 KB
aaac7fed   liuqimichale   add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  /*
  Copyright (c) 2014, Yahoo! Inc. All rights reserved.
  Copyrights licensed under the New BSD License.
  See the accompanying LICENSE file for terms.
  */
  
  'use strict';
  
  // Generate an internal UID to make the regexp pattern harder to guess.
  var UID                 = Math.floor(Math.random() * 0x10000000000).toString(16);
  var PLACE_HOLDER_REGEXP = new RegExp('"@__(F|R|D)-' + UID + '-(\\d+)__@"', 'g');
  
  var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g;
  var UNSAFE_CHARS_REGEXP   = /[<>\/\u2028\u2029]/g;
  
  // Mapping of unsafe HTML and invalid JavaScript line terminator chars to their
  // Unicode char counterparts which are safe to use in JavaScript strings.
  var ESCAPED_CHARS = {
      '<'     : '\\u003C',
      '>'     : '\\u003E',
      '/'     : '\\u002F',
      '\u2028': '\\u2028',
      '\u2029': '\\u2029'
  };
  
  function escapeUnsafeChars(unsafeChar) {
      return ESCAPED_CHARS[unsafeChar];
  }
  
  module.exports = function serialize(obj, options) {
      options || (options = {});
  
      // Backwards-compatibility for `space` as the second argument.
      if (typeof options === 'number' || typeof options === 'string') {
          options = {space: options};
      }
  
      var functions = [];
      var regexps   = [];
      var dates     = [];
  
      // Returns placeholders for functions and regexps (identified by index)
      // which are later replaced by their string representation.
      function replacer(key, value) {
          if (!value) {
              return value;
          }
  
          // If the value is an object w/ a toJSON method, toJSON is called before
          // the replacer runs, so we use this[key] to get the non-toJSONed value.
          var origValue = this[key];
          var type = typeof origValue;
  
          if (type === 'object') {
              if(origValue instanceof RegExp) {
                  return '@__R-' + UID + '-' + (regexps.push(origValue) - 1) + '__@';
              }
  
              if(origValue instanceof Date) {
                  return '@__D-' + UID + '-' + (dates.push(origValue) - 1) + '__@';
              }
          }
  
          if (type === 'function') {
              return '@__F-' + UID + '-' + (functions.push(origValue) - 1) + '__@';
          }
  
          return value;
      }
  
      var str;
  
      // Creates a JSON string representation of the value.
      // NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args.
      if (options.isJSON && !options.space) {
          str = JSON.stringify(obj);
      } else {
          str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space);
      }
  
      // Protects against `JSON.stringify()` returning `undefined`, by serializing
      // to the literal string: "undefined".
      if (typeof str !== 'string') {
          return String(str);
      }
  
      // Replace unsafe HTML and invalid JavaScript line terminator chars with
      // their safe Unicode char counterpart. This _must_ happen before the
      // regexps and functions are serialized and added back to the string.
      if (options.unsafe !== true) {
          str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);
      }
  
      if (functions.length === 0 && regexps.length === 0 && dates.length === 0) {
          return str;
      }
  
      // Replaces all occurrences of function, regexp and date placeholders in the
      // JSON string with their string representations. If the original value can
      // not be found, then `undefined` is used.
      return str.replace(PLACE_HOLDER_REGEXP, function (match, type, valueIndex) {
          if (type === 'D') {
              return "new Date(\"" + dates[valueIndex].toISOString() + "\")";
          }
  
          if (type === 'R') {
              return regexps[valueIndex].toString();
          }
  
          var fn           = functions[valueIndex];
          var serializedFn = fn.toString();
  
          if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {
              throw new TypeError('Serializing native function: ' + fn.name);
          }
  
          return serializedFn;
      });
  }