From 96e126cec4e9324b126f43e8971614d8db121763 Mon Sep 17 00:00:00 2001 From: "Matt R. Wilson" Date: Sat, 28 Mar 2020 16:45:25 -0600 Subject: [PATCH] refactor: replace LoDash isPlainObject, mapValues, and isMap - `isMap` was added to `util.types` in Node 10.0 https://nodejs.org/api/util.html#util_util_types_ismap_value - `isPlainObject` and `mapValue` both came from LoDash's master branch, which is the pending v5. Ref: #1285 --- lib/common.js | 114 ++++++++++++++++++++++++++++++++------------- lib/interceptor.js | 3 +- lib/match_body.js | 5 +- 3 files changed, 85 insertions(+), 37 deletions(-) diff --git a/lib/common.js b/lib/common.js index 6185ca5e3..1e5a591ea 100644 --- a/lib/common.js +++ b/lib/common.js @@ -2,8 +2,9 @@ const _ = require('lodash') const debug = require('debug')('nock.common') -const url = require('url') 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') } @@ -575,7 +576,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 } @@ -591,6 +592,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/master/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/master/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 = [] @@ -617,29 +663,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) }