es.json.stringify.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. var $ = require('../internals/export');
  2. var getBuiltIn = require('../internals/get-built-in');
  3. var apply = require('../internals/function-apply');
  4. var call = require('../internals/function-call');
  5. var uncurryThis = require('../internals/function-uncurry-this');
  6. var fails = require('../internals/fails');
  7. var isArray = require('../internals/is-array');
  8. var isCallable = require('../internals/is-callable');
  9. var isObject = require('../internals/is-object');
  10. var isSymbol = require('../internals/is-symbol');
  11. var arraySlice = require('../internals/array-slice');
  12. var NATIVE_SYMBOL = require('../internals/symbol-constructor-detection');
  13. var $stringify = getBuiltIn('JSON', 'stringify');
  14. var exec = uncurryThis(/./.exec);
  15. var charAt = uncurryThis(''.charAt);
  16. var charCodeAt = uncurryThis(''.charCodeAt);
  17. var replace = uncurryThis(''.replace);
  18. var numberToString = uncurryThis(1.0.toString);
  19. var tester = /[\uD800-\uDFFF]/g;
  20. var low = /^[\uD800-\uDBFF]$/;
  21. var hi = /^[\uDC00-\uDFFF]$/;
  22. var WRONG_SYMBOLS_CONVERSION = !NATIVE_SYMBOL || fails(function () {
  23. var symbol = getBuiltIn('Symbol')();
  24. // MS Edge converts symbol values to JSON as {}
  25. return $stringify([symbol]) != '[null]'
  26. // WebKit converts symbol values to JSON as null
  27. || $stringify({ a: symbol }) != '{}'
  28. // V8 throws on boxed symbols
  29. || $stringify(Object(symbol)) != '{}';
  30. });
  31. // https://github.com/tc39/proposal-well-formed-stringify
  32. var ILL_FORMED_UNICODE = fails(function () {
  33. return $stringify('\uDF06\uD834') !== '"\\udf06\\ud834"'
  34. || $stringify('\uDEAD') !== '"\\udead"';
  35. });
  36. var stringifyWithSymbolsFix = function (it, replacer) {
  37. var args = arraySlice(arguments);
  38. var $replacer = replacer;
  39. if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined
  40. if (!isArray(replacer)) replacer = function (key, value) {
  41. if (isCallable($replacer)) value = call($replacer, this, key, value);
  42. if (!isSymbol(value)) return value;
  43. };
  44. args[1] = replacer;
  45. return apply($stringify, null, args);
  46. };
  47. var fixIllFormed = function (match, offset, string) {
  48. var prev = charAt(string, offset - 1);
  49. var next = charAt(string, offset + 1);
  50. if ((exec(low, match) && !exec(hi, next)) || (exec(hi, match) && !exec(low, prev))) {
  51. return '\\u' + numberToString(charCodeAt(match, 0), 16);
  52. } return match;
  53. };
  54. if ($stringify) {
  55. // `JSON.stringify` method
  56. // https://tc39.es/ecma262/#sec-json.stringify
  57. $({ target: 'JSON', stat: true, arity: 3, forced: WRONG_SYMBOLS_CONVERSION || ILL_FORMED_UNICODE }, {
  58. // eslint-disable-next-line no-unused-vars -- required for `.length`
  59. stringify: function stringify(it, replacer, space) {
  60. var args = arraySlice(arguments);
  61. var result = apply(WRONG_SYMBOLS_CONVERSION ? stringifyWithSymbolsFix : $stringify, null, args);
  62. return ILL_FORMED_UNICODE && typeof result == 'string' ? replace(result, tester, fixIllFormed) : result;
  63. }
  64. });
  65. }