Skip to content

Commit

Permalink
feat: Add minimal Object.assign ponyfill (#379)
Browse files Browse the repository at this point in the history
since we can not rely on it being present in all supported runtimes.
Even though the interface is the same as `Object.assign`,
it behaves slightly differently from the one provided by browsers (see tests).

This was extracted from #338 to support development in #367
  • Loading branch information
karfau committed Feb 15, 2022
1 parent 9c2f64b commit 7c28171
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
26 changes: 26 additions & 0 deletions lib/conventions.js
Expand Up @@ -22,6 +22,31 @@ function freeze(object, oc) {
return oc && typeof oc.freeze === 'function' ? oc.freeze(object) : object
}

/**
* Since we can not rely on `Object.assign` we provide a simplified version
* that is sufficient for our needs.
*
* @param {Object} target
* @param {Object | null | undefined} source
*
* @returns {Object} target
* @throws TypeError if target is not an object
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
* @see https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign
*/
function assign(target, source) {
if (target === null || typeof target !== 'object') {
throw new TypeError('target is not an object')
}
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key]
}
}
return target
}

/**
* All mime types that are allowed as input to `DOMParser.parseFromString`
*
Expand Down Expand Up @@ -139,6 +164,7 @@ var NAMESPACE = freeze({
XMLNS: 'http://www.w3.org/2000/xmlns/',
})

exports.assign = assign;
exports.freeze = freeze;
exports.MIME_TYPE = MIME_TYPE;
exports.NAMESPACE = NAMESPACE;
52 changes: 52 additions & 0 deletions test/conventions/assign.test.js
@@ -0,0 +1,52 @@
'use strict'
const { assign } = require('../../lib/conventions')

describe('assign', () => {
test.each([null, undefined, true, false, 0, NaN])(
'should throw when `target` is `%s`',
(target) => {
expect(() => assign(target, {})).toThrow(TypeError)
}
)
test('should return target', () => {
const target = {}
expect(assign(target, undefined)).toBe(target)
})
test('should copy all enumerable fields from source to target', () => {
const target = {}
const source = { a: 'A', 0: 0 }

assign(target, source)

expect(target).toEqual(source)
})
test('should not copy prototype properties to source', () => {
const target = {}
function Clazz(yes) {
this.yes = yes
}
Clazz.prototype.dont = 5
Clazz.prototype.hasOwnProperty = () => true
const source = new Clazz(1)

assign(target, source)

expect(target).toEqual({ yes: 1 })
})
test('should have no issue with null source', () => {
const target = {}
assign(target, null)
})
test('should have no issue with undefined source', () => {
const target = {}
assign(target, undefined)
})
test('should override existing keys', () => {
const target = { key: 4, same: 'same' }
const source = { key: undefined }

assign(target, source)

expect(target).toEqual({ key: undefined, same: 'same' })
})
})

0 comments on commit 7c28171

Please sign in to comment.