From af27194689abfdc170ab7414d35c61156369208e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20W=C3=A4rting?= Date: Tue, 18 Jan 2022 18:49:54 +0100 Subject: [PATCH] refactor: added some utils fns (#1657) * move isObject to utils * impl and use hasOwn utility fn * impl a util mixin fn * remove the base mixin * use utils.mixin to inherit the base class --- src/client.js | 13 ++++++------- src/is-object.js | 13 ------------- src/node/index.js | 10 ++++++---- src/node/response.js | 5 +++-- src/request-base.js | 33 +++++++-------------------------- src/response-base.js | 21 +-------------------- src/utils.js | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 55 insertions(+), 72 deletions(-) delete mode 100644 src/is-object.js diff --git a/src/client.js b/src/client.js index 44247476e..0246ab422 100644 --- a/src/client.js +++ b/src/client.js @@ -21,7 +21,7 @@ const Emitter = require('component-emitter'); const safeStringify = require('fast-safe-stringify'); const qs = require('qs'); const RequestBase = require('./request-base'); -const isObject = require('./is-object'); +const { isObject, mixin, hasOwn } = require('./utils'); const ResponseBase = require('./response-base'); const Agent = require('./agent-base'); @@ -110,7 +110,7 @@ function serialize(object) { if (!isObject(object)) return object; const pairs = []; for (const key in object) { - if (Object.prototype.hasOwnProperty.call(object, key)) + if (hasOwn(object, key)) pushEncodedKeyValuePair(pairs, key, object[key]); } @@ -139,7 +139,7 @@ function pushEncodedKeyValuePair(pairs, key, value) { } } else if (isObject(value)) { for (const subkey in value) { - if (Object.prototype.hasOwnProperty.call(value, subkey)) + if (hasOwn(value, subkey)) pushEncodedKeyValuePair(pairs, `${key}[${subkey}]`, value[subkey]); } } else { @@ -361,8 +361,7 @@ function Response(request_) { } } -// eslint-disable-next-line new-cap -ResponseBase(Response.prototype); +mixin(Response.prototype, ResponseBase.prototype); /** * Parse the given body `str`. @@ -492,7 +491,7 @@ function Request(method, url) { // eslint-disable-next-line new-cap Emitter(Request.prototype); // eslint-disable-next-line new-cap -RequestBase(Request.prototype); +mixin(Request.prototype, RequestBase.prototype); /** * Set Content-Type to `type`, mapping values from `request.types`. @@ -875,7 +874,7 @@ Request.prototype._end = function () { for (const field in this.header) { if (this.header[field] === null) continue; - if (Object.prototype.hasOwnProperty.call(this.header, field)) + if (hasOwn(this.header, field)) xhr.setRequestHeader(field, this.header[field]); } diff --git a/src/is-object.js b/src/is-object.js deleted file mode 100644 index ff69f27b5..000000000 --- a/src/is-object.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Check if `obj` is an object. - * - * @param {Object} obj - * @return {Boolean} - * @api private - */ - -function isObject(object) { - return object !== null && typeof object === 'object'; -} - -module.exports = isObject; diff --git a/src/node/index.js b/src/node/index.js index d1fa2cf25..9190cd57d 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -25,6 +25,8 @@ const RequestBase = require('../request-base'); const { unzip } = require('./unzip'); const Response = require('./response'); +const { mixin, hasOwn } = utils; + let http2; if (semverGte(process.version, 'v10.10.0')) http2 = require('./http2wrapper'); @@ -174,7 +176,7 @@ function Request(method, url) { */ util.inherits(Request, Stream); // eslint-disable-next-line new-cap -RequestBase(Request.prototype); +mixin(Request.prototype, RequestBase.prototype); /** * Enable or Disable http2. @@ -829,13 +831,13 @@ Request.prototype.request = function () { } for (const key in this.header) { - if (Object.prototype.hasOwnProperty.call(this.header, key)) + if (hasOwn(this.header, key)) req.setHeader(key, this.header[key]); } // add cookies if (this.cookies) { - if (Object.prototype.hasOwnProperty.call(this._header, 'cookie')) { + if (hasOwn(this._header, 'cookie')) { // merge const temporaryJar = new CookieJar.CookieJar(); temporaryJar.setCookies(this._header.cookie.split(';')); @@ -1220,7 +1222,7 @@ Request.prototype._end = function () { // set headers const headers = formData.getHeaders(); for (const i in headers) { - if (Object.prototype.hasOwnProperty.call(headers, i)) { + if (hasOwn(headers, i)) { debug('setting FormData header: "%s: %s"', i, headers[i]); req.setHeader(i, headers[i]); } diff --git a/src/node/response.js b/src/node/response.js index 8f7fb08f5..d5fe4c682 100644 --- a/src/node/response.js +++ b/src/node/response.js @@ -5,6 +5,7 @@ const util = require('util'); const Stream = require('stream'); const ResponseBase = require('../response-base'); +const { mixin } = require('../utils'); /** * Expose `Response`. @@ -52,8 +53,8 @@ function Response(request) { */ util.inherits(Response, Stream); -// eslint-disable-next-line new-cap -ResponseBase(Response.prototype); + +mixin(Response.prototype, ResponseBase.prototype); /** * Implements methods of a `ReadableStream` diff --git a/src/request-base.js b/src/request-base.js index 176aee7c3..636d0c28f 100644 --- a/src/request-base.js +++ b/src/request-base.js @@ -3,7 +3,7 @@ const semver = require('semver'); /** * Module of mixed-in functions shared between node and client code */ -const isObject = require('./is-object'); +const { isObject, hasOwn } = require('./utils'); /** * Expose `RequestBase`. @@ -17,26 +17,7 @@ module.exports = RequestBase; * @api public */ -function RequestBase(object) { - if (object) return mixin(object); -} - -/** - * Mixin the prototype properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - -function mixin(object) { - for (const key in RequestBase.prototype) { - if (Object.prototype.hasOwnProperty.call(RequestBase.prototype, key)) - object[key] = RequestBase.prototype[key]; - } - - return object; -} +function RequestBase() {} /** * Clear previous timeout. @@ -129,7 +110,7 @@ RequestBase.prototype.timeout = function (options) { } for (const option in options) { - if (Object.prototype.hasOwnProperty.call(options, option)) { + if (hasOwn(options, option)) { switch (option) { case 'deadline': this._timeout = options.deadline; @@ -393,7 +374,7 @@ RequestBase.prototype.getHeader = RequestBase.prototype.get; RequestBase.prototype.set = function (field, value) { if (isObject(field)) { for (const key in field) { - if (Object.prototype.hasOwnProperty.call(field, key)) + if (hasOwn(field, key)) this.set(key, field[key]); } @@ -456,7 +437,7 @@ RequestBase.prototype.field = function (name, value) { if (isObject(name)) { for (const key in name) { - if (Object.prototype.hasOwnProperty.call(name, key)) + if (hasOwn(name, key)) this.field(key, name[key]); } @@ -465,7 +446,7 @@ RequestBase.prototype.field = function (name, value) { if (Array.isArray(value)) { for (const i in value) { - if (Object.prototype.hasOwnProperty.call(value, i)) + if (hasOwn(value, i)) this.field(name, value[i]); } @@ -683,7 +664,7 @@ RequestBase.prototype.send = function (data) { // merge if (isObject_ && isObject(this._data)) { for (const key in data) { - if (Object.prototype.hasOwnProperty.call(data, key)) + if (hasOwn(data, key)) this._data[key] = data[key]; } } else if (typeof data === 'string') { diff --git a/src/response-base.js b/src/response-base.js index da07ec39e..c47b748d0 100644 --- a/src/response-base.js +++ b/src/response-base.js @@ -16,26 +16,7 @@ module.exports = ResponseBase; * @api public */ -function ResponseBase(object) { - if (object) return mixin(object); -} - -/** - * Mixin the prototype properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - -function mixin(object) { - for (const key in ResponseBase.prototype) { - if (Object.prototype.hasOwnProperty.call(ResponseBase.prototype, key)) - object[key] = ResponseBase.prototype[key]; - } - - return object; -} +function ResponseBase() {} /** * Get case-insensitive `field` value. diff --git a/src/utils.js b/src/utils.js index a9041462e..636e8fa3f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -70,3 +70,35 @@ exports.cleanHeader = (header, changesOrigin) => { return header; }; + +/** + * Check if `obj` is an object. + * + * @param {Object} object + * @return {Boolean} + * @api private + */ +exports.isObject = (object) => { + return object !== null && typeof object === 'object'; +} + +/** + * Object.hasOwn fallback/polyfill. + * + * @type {(object: object, property: string) => boolean} object + * @api private + */ +exports.hasOwn = Object.hasOwn || function (object, property) { + if (object == null) { + throw new TypeError("Cannot convert undefined or null to object") + } + return Object.prototype.hasOwnProperty.call(Object(object), property) +} + +exports.mixin = (target, source) => { + for (const key in source) { + if (exports.hasOwn(source, key)) { + target[key] = source[key]; + } + } +}