Skip to content

Commit 7bfcb31

Browse files
authored
fix(deps): remove lodash (#2529)
1 parent 0ba089c commit 7bfcb31

5 files changed

Lines changed: 195 additions & 18 deletions

File tree

lib/common.js

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
'use strict'
22

33
const debug = require('debug')('nock.common')
4-
const isPlainObject = require('lodash/isPlainObject')
5-
const set = require('lodash/set')
64
const timers = require('timers')
75
const url = require('url')
86
const util = require('util')
@@ -568,17 +566,6 @@ const dataEqual = (expected, actual) => {
568566
return deepEqual(expected, actual)
569567
}
570568

571-
/**
572-
* Converts flat objects whose keys use JSON path notation to nested objects.
573-
*
574-
* The input object is not mutated.
575-
*
576-
* @example
577-
* { 'foo[bar][0]': 'baz' } -> { foo: { bar: [ 'baz' ] } }
578-
*/
579-
const expand = input =>
580-
Object.entries(input).reduce((acc, [k, v]) => set(acc, k, v), {})
581-
582569
/**
583570
* Performs a recursive strict comparison between two values.
584571
*
@@ -665,10 +652,97 @@ function isRequestDestroyed(req) {
665652
)
666653
}
667654

655+
/**
656+
* Returns true if the given value is a plain object and not an Array.
657+
* @param {*} value
658+
* @returns {boolean}
659+
*/
660+
function isPlainObject(value) {
661+
if (typeof value !== 'object' || value === null) return false
662+
663+
if (Object.prototype.toString.call(value) !== '[object Object]') return false
664+
665+
const proto = Object.getPrototypeOf(value)
666+
if (proto === null) return true
667+
668+
const Ctor =
669+
Object.prototype.hasOwnProperty.call(proto, 'constructor') &&
670+
proto.constructor
671+
return (
672+
typeof Ctor === 'function' &&
673+
Ctor instanceof Ctor &&
674+
Function.prototype.call(Ctor) === Function.prototype.call(value)
675+
)
676+
}
677+
678+
const prototypePollutionBlockList = ['__proto__', 'prototype', 'constructor']
679+
const blocklistFilter = function (part) {
680+
return prototypePollutionBlockList.indexOf(part) === -1
681+
}
682+
683+
/**
684+
* Converts flat objects whose keys use JSON path notation to nested objects.
685+
*
686+
* The input object is not mutated.
687+
*
688+
* @example
689+
* { 'foo[bar][0]': 'baz' } -> { foo: { bar: [ 'baz' ] } }
690+
*/
691+
const expand = input => {
692+
if (input === undefined || input === null) {
693+
return input
694+
}
695+
696+
const keys = Object.keys(input)
697+
698+
const result = {}
699+
let resultPtr = result
700+
701+
for (let path of keys) {
702+
const originalPath = path
703+
if (path.indexOf('[') >= 0) {
704+
path = path.replace(/\[/g, '.').replace(/]/g, '')
705+
}
706+
707+
const parts = path.split('.')
708+
709+
const check = parts.filter(blocklistFilter)
710+
711+
if (check.length !== parts.length) {
712+
return undefined
713+
}
714+
resultPtr = result
715+
const lastIndex = parts.length - 1
716+
717+
for (let i = 0; i < parts.length; ++i) {
718+
const part = parts[i]
719+
if (i === lastIndex) {
720+
if (Array.isArray(resultPtr)) {
721+
resultPtr[+part] = input[originalPath]
722+
} else {
723+
resultPtr[part] = input[originalPath]
724+
}
725+
} else {
726+
if (resultPtr[part] === undefined || resultPtr[part] === null) {
727+
const nextPart = parts[i + 1]
728+
if (/^\d+$/.test(nextPart)) {
729+
resultPtr[part] = []
730+
} else {
731+
resultPtr[part] = {}
732+
}
733+
}
734+
resultPtr = resultPtr[part]
735+
}
736+
}
737+
}
738+
return result
739+
}
740+
668741
module.exports = {
669742
contentEncoding,
670743
dataEqual,
671744
deleteHeadersField,
745+
expand,
672746
forEachHeader,
673747
formatQueryValue,
674748
headersArrayToObject,

lib/match_body.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict'
22

3-
const mapValues = require('lodash/mapValues')
43
const querystring = require('querystring')
54

65
const common = require('./common')
@@ -62,6 +61,14 @@ module.exports = function matchBody(options, spec, body) {
6261
return common.dataEqual(spec, body)
6362
}
6463

64+
function mapValues(object, cb) {
65+
const keys = Object.keys(object)
66+
for (const key of keys) {
67+
object[key] = cb(object[key], key, object)
68+
}
69+
return object
70+
}
71+
6572
/**
6673
* Based on lodash issue discussion
6774
* https://github.com/lodash/lodash/issues/1244

package-lock.json

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
"dependencies": {
2525
"debug": "^4.1.0",
2626
"json-stringify-safe": "^5.0.1",
27-
"lodash": "^4.17.21",
2827
"propagate": "^2.0.0"
2928
},
3029
"devDependencies": {

tests/test_common.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,3 +545,99 @@ it('testing timers are deleted correctly', done => {
545545
done()
546546
})
547547
})
548+
549+
describe('`isPlainObject()`', () => {
550+
const { isPlainObject } = common
551+
552+
it('custom Object', () => {
553+
function Foo() {
554+
this.a = 1
555+
}
556+
expect(isPlainObject(new Foo()), false)
557+
})
558+
559+
it('Array', () => {
560+
expect(isPlainObject([1, 2, 3]), false)
561+
})
562+
563+
it('Date', () => {
564+
expect(isPlainObject(new Date()), false)
565+
})
566+
567+
it('RegExp', () => {
568+
expect(isPlainObject(/a/), false)
569+
})
570+
571+
it('plain Object', () => {
572+
expect(isPlainObject({}), true)
573+
})
574+
575+
it('null', () => {
576+
expect(isPlainObject(null), true)
577+
})
578+
579+
it('null-Object /1', () => {
580+
expect(isPlainObject({ __proto__: null }), true)
581+
})
582+
583+
it('null-Object /2', () => {
584+
expect(isPlainObject(Object.create(null)), true)
585+
})
586+
})
587+
588+
describe('`expand()`', () => {
589+
const { expand } = common
590+
591+
it('undefined', () => {
592+
expect(expand(undefined), undefined)
593+
})
594+
595+
it('null', () => {
596+
expect(expand(null), null)
597+
})
598+
599+
it('throws on constructor', () => {
600+
expect(expand({ constructor: 4 })).equal(undefined)
601+
})
602+
603+
it('pure key values', () => {
604+
expect(expand({ a: 4 })).deep.equal({ a: 4 })
605+
})
606+
607+
it('nested object', () => {
608+
expect(expand({ 'a.b': 4 })).deep.equal({ a: { b: 4 } })
609+
})
610+
611+
it('nested object', () => {
612+
expect(expand({ 'a.b': 4, 'a.c': 5 })).deep.equal({ a: { b: 4, c: 5 } })
613+
})
614+
615+
it('nested object', () => {
616+
expect(expand({ 'a.b': 4, 'b.a': 5 })).deep.equal({
617+
a: { b: 4 },
618+
b: { a: 5 },
619+
})
620+
})
621+
622+
it('nested array', () => {
623+
expect(expand({ 'a.0': 4, 'a.1': 5 })).deep.equal({ a: [4, 5] })
624+
})
625+
626+
it('array-like', () => {
627+
expect(expand({ 'a[0]': 4, 'a[1]': 5 })).deep.equal({ a: [4, 5] })
628+
})
629+
630+
it('example', () => {
631+
expect(expand({ 'foo[bar][0]': 'baz' })).deep.equal({
632+
foo: { bar: ['baz'] },
633+
})
634+
})
635+
636+
it('does not mutate original', () => {
637+
const original = { 'foo[bar][0]': 'baz' }
638+
const result = expand(original)
639+
expect(result).deep.equal({ foo: { bar: ['baz'] } })
640+
expect(original).deep.equal({ 'foo[bar][0]': 'baz' })
641+
expect(original).not.equal(result)
642+
})
643+
})

0 commit comments

Comments
 (0)