diff --git a/lib/common.js b/lib/common.js index 7679c0a38..259c6e211 100644 --- a/lib/common.js +++ b/lib/common.js @@ -1,9 +1,10 @@ 'use strict' -const _ = require('lodash') const debug = require('debug')('nock.common') -const url = require('url') +const set = require('lodash.set') const timers = require('timers') +const url = require('url') +const util = require('util') /** * Normalizes the request options so that it always has `host` property. @@ -194,7 +195,7 @@ function isJSONContent(headers) { * Duplicates throw an error. */ function headersFieldNamesToLowerCase(headers) { - if (!_.isPlainObject(headers)) { + if (!isPlainObject(headers)) { throw Error('Headers must be provided as an object') } @@ -243,11 +244,11 @@ function headersInputToRawArray(headers) { } // [].concat(...) is used instead of Array.flat until v11 is the minimum Node version - if (_.isMap(headers)) { + if (util.types.isMap(headers)) { return [].concat(...Array.from(headers, ([k, v]) => [k.toString(), v])) } - if (_.isPlainObject(headers)) { + if (isPlainObject(headers)) { return [].concat(...Object.entries(headers)) } @@ -359,7 +360,7 @@ function addHeaderLine(headers, name, value) { * @fieldName {String} field name - string with the case-insensitive field name */ function deleteHeadersField(headers, fieldNameToDelete) { - if (!_.isPlainObject(headers)) { + if (!isPlainObject(headers)) { throw Error('headers must be an object') } @@ -559,7 +560,7 @@ const dataEqual = (expected, actual) => * { 'foo[bar][0]': 'baz' } -> { foo: { bar: [ 'baz' ] } } */ const expand = input => - Object.entries(input).reduce((acc, [k, v]) => _.set(acc, k, v), {}) + Object.entries(input).reduce((acc, [k, v]) => set(acc, k, v), {}) /** * Performs a recursive strict comparison between two values. @@ -572,7 +573,7 @@ function deepEqual(expected, actual) { return expected.test(actual) } - if (Array.isArray(expected) || _.isPlainObject(expected)) { + if (Array.isArray(expected) || isPlainObject(expected)) { if (actual === undefined) { return false } @@ -588,6 +589,51 @@ function deepEqual(expected, actual) { return expected === actual } +/** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * https://github.com/lodash/lodash/blob/588bf3e20db0ae039a822a14a8fa238c5b298e65/isPlainObject.js + * + * @param {*} value The value to check. + * @return {boolean} + */ +function isPlainObject(value) { + const isObjectLike = typeof value === 'object' && value !== null + const tag = Object.prototype.toString.call(value) + if (!isObjectLike || tag !== '[object Object]') { + return false + } + if (Object.getPrototypeOf(value) === null) { + return true + } + let proto = value + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto) + } + return Object.getPrototypeOf(value) === proto +} + +/** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. (iteration order is not guaranteed) + * The iteratee is invoked with three arguments: (value, key, object). + * https://github.com/lodash/lodash/blob/588bf3e20db0ae039a822a14a8fa238c5b298e65/mapValue.js + * + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + */ +function mapValue(object, iteratee) { + object = Object(object) + const result = {} + + Object.keys(object).forEach(key => { + result[key] = iteratee(object[key], key, object) + }) + return result +} + const timeouts = [] const intervals = [] const immediates = [] @@ -614,29 +660,33 @@ function removeAllTimers() { clearTimer(clearImmediate, immediates) } -exports.normalizeClientRequestArgs = normalizeClientRequestArgs -exports.normalizeRequestOptions = normalizeRequestOptions -exports.normalizeOrigin = normalizeOrigin -exports.isUtf8Representable = isUtf8Representable -exports.overrideRequests = overrideRequests -exports.restoreOverriddenRequests = restoreOverriddenRequests -exports.stringifyRequest = stringifyRequest -exports.isContentEncoded = isContentEncoded -exports.contentEncoding = contentEncoding -exports.isJSONContent = isJSONContent -exports.headersFieldNamesToLowerCase = headersFieldNamesToLowerCase -exports.headersFieldsArrayToLowerCase = headersFieldsArrayToLowerCase -exports.headersArrayToObject = headersArrayToObject -exports.headersInputToRawArray = headersInputToRawArray -exports.deleteHeadersField = deleteHeadersField -exports.forEachHeader = forEachHeader -exports.percentEncode = percentEncode -exports.percentDecode = percentDecode -exports.matchStringOrRegexp = matchStringOrRegexp -exports.formatQueryValue = formatQueryValue -exports.isStream = isStream -exports.dataEqual = dataEqual -exports.setTimeout = setTimeout -exports.setInterval = setInterval -exports.setImmediate = setImmediate -exports.removeAllTimers = removeAllTimers +module.exports = { + contentEncoding, + dataEqual, + deleteHeadersField, + forEachHeader, + formatQueryValue, + headersArrayToObject, + headersFieldNamesToLowerCase, + headersFieldsArrayToLowerCase, + headersInputToRawArray, + isContentEncoded, + isJSONContent, + isPlainObject, + isStream, + isUtf8Representable, + mapValue, + matchStringOrRegexp, + normalizeClientRequestArgs, + normalizeOrigin, + normalizeRequestOptions, + overrideRequests, + percentDecode, + percentEncode, + removeAllTimers, + restoreOverriddenRequests, + setImmediate, + setInterval, + setTimeout, + stringifyRequest, +} diff --git a/lib/interceptor.js b/lib/interceptor.js index 8d1cccba0..202c43bb3 100644 --- a/lib/interceptor.js +++ b/lib/interceptor.js @@ -2,7 +2,6 @@ const debug = require('debug')('nock.interceptor') const stringify = require('json-stringify-safe') -const _ = require('lodash') const querystring = require('querystring') const { URL, URLSearchParams } = require('url') @@ -483,7 +482,7 @@ module.exports = class Interceptor { // Normalize the data into the shape that is matched against. // Duplicate keys are handled by combining the values into an array. queries = querystring.parse(queries.toString()) - } else if (!_.isPlainObject(queries)) { + } else if (!common.isPlainObject(queries)) { throw Error(`Argument Error: ${queries}`) } diff --git a/lib/match_body.js b/lib/match_body.js index 6eda62dd5..73f753a23 100644 --- a/lib/match_body.js +++ b/lib/match_body.js @@ -1,6 +1,5 @@ 'use strict' -const _ = require('lodash') const querystring = require('querystring') const common = require('./common') @@ -70,8 +69,8 @@ function mapValuesDeep(obj, cb) { if (Array.isArray(obj)) { return obj.map(v => mapValuesDeep(v, cb)) } - if (_.isPlainObject(obj)) { - return _.mapValues(obj, v => mapValuesDeep(v, cb)) + if (common.isPlainObject(obj)) { + return common.mapValue(obj, v => mapValuesDeep(v, cb)) } return cb(obj) } diff --git a/package-lock.json b/package-lock.json index 1c0f480b2..1efc2d94d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3935,7 +3935,8 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true }, "lodash.capitalize": { "version": "4.2.1", @@ -3981,9 +3982,8 @@ }, "lodash.set": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true + "resolved": "http://artifactory.shuttercorp.net/artifactory/api/npm/npm-composite/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" }, "lodash.toarray": { "version": "4.4.0", diff --git a/package.json b/package.json index ea222e26d..00de7a5a9 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "dependencies": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.13", + "lodash.set": "^4.3.2", "propagate": "^2.0.0" }, "devDependencies": {