Skip to content

Commit ae85b3a

Browse files
authored
feat: Vendor prod dependency on json-stringify-safe (#2910)
1 parent c616e59 commit ae85b3a

5 files changed

Lines changed: 156 additions & 5 deletions

File tree

lib/interceptor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
'use strict'
22

33
const fs = require('node:fs')
4-
const stringify = require('json-stringify-safe')
54
const querystring = require('node:querystring')
65
const { URL, URLSearchParams } = require('node:url')
76

7+
const stringify = require('./stringify')
88
const common = require('./common')
99
const { remove } = require('./intercept')
1010
const matchBody = require('./match_body')

lib/stringify.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict'
2+
/**
3+
* @see https://github.com/moll/json-stringify-safe/blob/02cfafd45f06d076ac4bf0dd28be6738a07a72f9/stringify.js
4+
* @license
5+
* The ISC License
6+
*
7+
* Copyright (c) Isaac Z. Schlueter and Contributors
8+
*
9+
* Permission to use, copy, modify, and/or distribute this software for any
10+
* purpose with or without fee is hereby granted, provided that the above
11+
* copyright notice and this permission notice appear in all copies.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
19+
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20+
*/
21+
22+
/**
23+
* @param {*} obj
24+
* @returns {string}
25+
*/
26+
function stringify(obj) {
27+
return JSON.stringify(obj, safeReplacer())
28+
}
29+
30+
/**
31+
* @param {Array<*>} stack
32+
* @param {Array<string>} keys
33+
* @param {*} value
34+
* @returns {string}
35+
*/
36+
function cycleReplacer(stack, keys, value) {
37+
if (stack[0] === value) return '[Circular ~]'
38+
return `[Circular ~.${keys.slice(0, stack.indexOf(value)).join('.')}]`
39+
}
40+
41+
/**
42+
* @param {Array<*>} [stack]
43+
* @param {Array<string>} [keys]
44+
* @returns {(key: string, value: *) => *} */
45+
function safeReplacer(stack = [], keys = []) {
46+
return function (key, value) {
47+
if (stack.length > 0) {
48+
const thisPos = stack.indexOf(this)
49+
~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
50+
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
51+
if (~stack.indexOf(value)) {
52+
value = cycleReplacer(stack, keys, value)
53+
}
54+
} else {
55+
stack.push(value)
56+
}
57+
58+
return value
59+
}
60+
}
61+
62+
module.exports = stringify

package-lock.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
"main": "./index.js",
2323
"types": "types",
2424
"dependencies": {
25-
"@mswjs/interceptors": "^0.41.2",
26-
"json-stringify-safe": "^5.0.1"
25+
"@mswjs/interceptors": "^0.41.2"
2726
},
2827
"devDependencies": {
2928
"@definitelytyped/dtslint": "^0.0.163",

tests/test_stringify.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use strict'
2+
3+
const { expect } = require('chai')
4+
const stringify = require('../lib/stringify')
5+
6+
describe('stringify', () => {
7+
it('should stringify a number', () => {
8+
expect(stringify(42)).to.equal('42')
9+
})
10+
11+
it('should stringify a string', () => {
12+
expect(stringify('foo')).to.equal('"foo"')
13+
})
14+
15+
it('should stringify a boolean', () => {
16+
expect(stringify(true)).to.equal('true')
17+
})
18+
19+
it('should stringify null', () => {
20+
expect(stringify(null)).to.equal('null')
21+
})
22+
23+
it('should stringify an object', () => {
24+
expect(stringify({ foo: 'bar' })).to.equal('{"foo":"bar"}')
25+
})
26+
27+
it('should stringify an array', () => {
28+
expect(stringify([1, 2, 3])).to.equal('[1,2,3]')
29+
})
30+
31+
it('should handle a circular reference in an object', () => {
32+
const obj = { foo: 'bar' }
33+
obj.self = obj
34+
expect(stringify(obj)).to.equal('{"foo":"bar","self":"[Circular ~]"}')
35+
})
36+
37+
it('should handle a circular reference in an array', () => {
38+
const arr = [1, 2, 3]
39+
arr.push(arr)
40+
expect(stringify(arr)).to.equal('[1,2,3,"[Circular ~]"]')
41+
})
42+
43+
it('should handle a nested circular reference', () => {
44+
const obj = { name: 'parent' }
45+
obj.child = { name: 'child', parent: obj }
46+
expect(stringify(obj)).to.equal(
47+
'{"name":"parent","child":{"name":"child","parent":"[Circular ~]"}}',
48+
)
49+
})
50+
51+
it('should handle a deeply nested circular reference with path notation', () => {
52+
const obj = { level1: { level2: { level3: {} } } }
53+
obj.level1.level2.level3.self = obj.level1
54+
expect(stringify(obj)).to.equal(
55+
'{"level1":{"level2":{"level3":{"self":"[Circular ~.level1]"}}}}',
56+
)
57+
})
58+
59+
it('should handle multiple references to the same object', () => {
60+
const shared = { value: 'shared' }
61+
const obj = { a: shared, b: shared }
62+
expect(stringify(obj)).to.equal(
63+
'{"a":{"value":"shared"},"b":{"value":"shared"}}',
64+
)
65+
})
66+
67+
it('should handle an object with a toJSON method that returns a circular reference', () => {
68+
const obj = {
69+
name: 'circular',
70+
toJSON() {
71+
return this
72+
},
73+
}
74+
expect(stringify(obj)).to.equal('{"name":"circular"}')
75+
})
76+
77+
it('should handle an object with circular references in different properties', () => {
78+
const obj = { a: {}, b: {} }
79+
obj.a.self = obj.a
80+
obj.b.self = obj.b
81+
expect(stringify(obj)).to.equal(
82+
'{"a":{"self":"[Circular ~.a]"},"b":{"self":"[Circular ~.b]"}}',
83+
)
84+
})
85+
86+
it('should handle a circular reference inside a nested array', () => {
87+
const arr = [1, [2, 3]]
88+
arr[1].push(arr)
89+
expect(stringify(arr)).to.equal('[1,[2,3,"[Circular ~]"]]')
90+
})
91+
})

0 commit comments

Comments
 (0)