index.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const stringify_1 = require("./stringify");
  4. const quote_1 = require("./quote");
  5. /**
  6. * Root path node.
  7. */
  8. const ROOT_SENTINEL = Symbol("root");
  9. /**
  10. * Stringify any JavaScript value.
  11. */
  12. function stringify(value, replacer, indent, options = {}) {
  13. const space = typeof indent === "string" ? indent : " ".repeat(indent || 0);
  14. const path = [];
  15. const stack = new Set();
  16. const tracking = new Map();
  17. const unpack = new Map();
  18. let valueCount = 0;
  19. const { maxDepth = 100, references = false, skipUndefinedProperties = false, maxValues = 100000 } = options;
  20. // Wrap replacer function to support falling back on supported stringify.
  21. const valueToString = replacerToString(replacer);
  22. // Every time you call `next(value)` execute this function.
  23. const onNext = (value, key) => {
  24. if (++valueCount > maxValues)
  25. return;
  26. if (skipUndefinedProperties && value === undefined)
  27. return;
  28. if (path.length > maxDepth)
  29. return;
  30. // An undefined key is treated as an out-of-band "value".
  31. if (key === undefined)
  32. return valueToString(value, space, onNext, key);
  33. path.push(key);
  34. const result = builder(value, key === ROOT_SENTINEL ? undefined : key);
  35. path.pop();
  36. return result;
  37. };
  38. const builder = references
  39. ? (value, key) => {
  40. if (value !== null &&
  41. (typeof value === "object" ||
  42. typeof value === "function" ||
  43. typeof value === "symbol")) {
  44. // Track nodes to restore later.
  45. if (tracking.has(value)) {
  46. unpack.set(path.slice(1), tracking.get(value));
  47. return; // Avoid serializing referenced nodes on an expression.
  48. }
  49. // Track encountered nodes.
  50. tracking.set(value, path.slice(1));
  51. }
  52. return valueToString(value, space, onNext, key);
  53. }
  54. : (value, key) => {
  55. // Stop on recursion.
  56. if (stack.has(value))
  57. return;
  58. stack.add(value);
  59. const result = valueToString(value, space, onNext, key);
  60. stack.delete(value);
  61. return result;
  62. };
  63. const result = onNext(value, ROOT_SENTINEL);
  64. // Attempt to restore circular references.
  65. if (unpack.size) {
  66. const sp = space ? " " : "";
  67. const eol = space ? "\n" : "";
  68. let wrapper = `var x${sp}=${sp}${result};${eol}`;
  69. for (const [key, value] of unpack.entries()) {
  70. const keyPath = quote_1.stringifyPath(key, onNext);
  71. const valuePath = quote_1.stringifyPath(value, onNext);
  72. wrapper += `x${keyPath}${sp}=${sp}x${valuePath};${eol}`;
  73. }
  74. return `(function${sp}()${sp}{${eol}${wrapper}return x;${eol}}())`;
  75. }
  76. return result;
  77. }
  78. exports.stringify = stringify;
  79. /**
  80. * Create `toString()` function from replacer.
  81. */
  82. function replacerToString(replacer) {
  83. if (!replacer)
  84. return stringify_1.toString;
  85. return (value, space, next, key) => {
  86. return replacer(value, space, (value) => stringify_1.toString(value, space, next, key), key);
  87. };
  88. }
  89. //# sourceMappingURL=index.js.map