diff --git a/packages/immutadot-lodash/src/array/pull.js b/packages/immutadot-lodash/src/array/pull.js index f82538b6..f5698a05 100644 --- a/packages/immutadot-lodash/src/array/pull.js +++ b/packages/immutadot-lodash/src/array/pull.js @@ -1,5 +1,5 @@ import _pull from 'lodash/fp/pull' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * Replaces an array removing all given values from the former array. diff --git a/packages/immutadot-lodash/src/array/pullAll.js b/packages/immutadot-lodash/src/array/pullAll.js index b7a5676f..443e3e5f 100644 --- a/packages/immutadot-lodash/src/array/pullAll.js +++ b/packages/immutadot-lodash/src/array/pullAll.js @@ -1,5 +1,5 @@ import _pullAll from 'lodash/fp/pullAll' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * This method is like {@link array.pull} except that it accepts an array of values to remove. diff --git a/packages/immutadot-lodash/src/array/pullAllBy.js b/packages/immutadot-lodash/src/array/pullAllBy.js index 8672440a..4084dee9 100644 --- a/packages/immutadot-lodash/src/array/pullAllBy.js +++ b/packages/immutadot-lodash/src/array/pullAllBy.js @@ -1,5 +1,5 @@ import _pullAllBy from 'lodash/fp/pullAllBy' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * This method is like {@link array.pullAll} except that it accepts iteratee to generate the criterion by which each element is compared. diff --git a/packages/immutadot-lodash/src/array/pullAllWith.js b/packages/immutadot-lodash/src/array/pullAllWith.js index d7709301..81f7b7ae 100644 --- a/packages/immutadot-lodash/src/array/pullAllWith.js +++ b/packages/immutadot-lodash/src/array/pullAllWith.js @@ -1,5 +1,5 @@ import _pullAllWith from 'lodash/fp/pullAllWith' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * This method is like {@link array.pullAll} except that it accepts comparator to compare elements. diff --git a/packages/immutadot-lodash/src/array/pullAt.js b/packages/immutadot-lodash/src/array/pullAt.js index 29b19e88..3e0e4223 100644 --- a/packages/immutadot-lodash/src/array/pullAt.js +++ b/packages/immutadot-lodash/src/array/pullAt.js @@ -1,5 +1,5 @@ import _pullAt from 'lodash/fp/pullAt' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * Replaces an array removing the specified indexes from the former array. diff --git a/packages/immutadot-lodash/src/array/remove.js b/packages/immutadot-lodash/src/array/remove.js index daa19cd8..ffd04f02 100644 --- a/packages/immutadot-lodash/src/array/remove.js +++ b/packages/immutadot-lodash/src/array/remove.js @@ -1,5 +1,5 @@ import _remove from 'lodash/fp/remove' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * Replaces an array removing elements that predicate returns truthy for from the former array. diff --git a/packages/immutadot-lodash/src/object/defaults.js b/packages/immutadot-lodash/src/object/defaults.js index c1d563c1..c0424f53 100644 --- a/packages/immutadot-lodash/src/object/defaults.js +++ b/packages/immutadot-lodash/src/object/defaults.js @@ -1,5 +1,5 @@ import _defaults from 'lodash/fp/defaults' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * Replaces by an object assigning own and inherited enumerable string keyed properties of source objects to the destination object for all destination properties that resolve to undefined.
diff --git a/packages/immutadot-lodash/src/object/merge.js b/packages/immutadot-lodash/src/object/merge.js index 018d118c..2e93ecbc 100644 --- a/packages/immutadot-lodash/src/object/merge.js +++ b/packages/immutadot-lodash/src/object/merge.js @@ -1,5 +1,5 @@ import _merge from 'lodash/fp/merge' -import { convertLodashFp } from 'immutadot/util/convert' +import { convertLodashFp } from 'util/convertLodashFp' /** * Replaces by an object deeply merging own enumerable string keyed properties of source objects to the former object.
diff --git a/packages/immutadot/src/util/convert.js b/packages/immutadot-lodash/src/util/convertLodashFp.js similarity index 91% rename from packages/immutadot/src/util/convert.js rename to packages/immutadot-lodash/src/util/convertLodashFp.js index 509bda54..d7b97b1c 100644 --- a/packages/immutadot/src/util/convert.js +++ b/packages/immutadot-lodash/src/util/convertLodashFp.js @@ -1,4 +1,4 @@ -import { convert } from 'core/convert' +import { convert } from 'immutadot/core/convert' import { lodashFpConvert } from './lodashFpConvert' /** diff --git a/packages/immutadot/src/util/lodashFpConvert.js b/packages/immutadot-lodash/src/util/lodashFpConvert.js similarity index 100% rename from packages/immutadot/src/util/lodashFpConvert.js rename to packages/immutadot-lodash/src/util/lodashFpConvert.js diff --git a/packages/immutadot/package.json b/packages/immutadot/package.json index 60ee1a0c..0cc3ac30 100644 --- a/packages/immutadot/package.json +++ b/packages/immutadot/package.json @@ -29,9 +29,6 @@ "jsdoc": "^3.5.5" }, "dependencies": {}, - "peerDependencies": { - "lodash": "^4.17.4" - }, "scripts": { "generate:flow": "node ../../misc/generate-flow.js", "prebuild": "yarn generate:flow", diff --git a/packages/immutadot/src/array/concat.js b/packages/immutadot/src/array/concat.js index 407ca6f9..1dc0cfea 100644 --- a/packages/immutadot/src/array/concat.js +++ b/packages/immutadot/src/array/concat.js @@ -1,5 +1,4 @@ -import _concat from 'lodash/concat' -import { convert } from 'core/convert' +import { convertArrayMethod } from './convertArrayMethod' /** * Replaces an array concatenating the former array with additional arrays and/or values. @@ -14,5 +13,5 @@ import { convert } from 'core/convert' * @since 0.2.0 * @flow */ -const concat = convert(_concat) +const concat = convertArrayMethod('concat', false, true) export { concat } diff --git a/packages/immutadot/src/array/convertArrayMethod.js b/packages/immutadot/src/array/convertArrayMethod.js index cdcc4e5f..d62f7e13 100644 --- a/packages/immutadot/src/array/convertArrayMethod.js +++ b/packages/immutadot/src/array/convertArrayMethod.js @@ -4,25 +4,39 @@ import { isNil, } from 'util/lang' -const copyArray = array => { +const toArray = array => { + if (isNil(array)) return [] + if (Array.isArray(array)) return array + return [array] +} + +const toArrayCopy = array => { if (isNil(array)) return [] if (Array.isArray(array)) return [...array] return [array] } +const callMethodReturnResult = (array, method, args) => array[method](...args) + +const callMethodReturnArray = (array, method, args) => { + array[method](...args) + return array +} + /** * Converts an Array method. * @memberof array * @param {string} method Array method name. + * @param {boolean} [mutating=true] Whether the method mutates the array. * @return {function} Returns the wrapped function. * @see {@link core.convert|convert} for more information. * @since 0.2.0 * @private */ -const convertArrayMethod = method => convert((array, ...args) => { - const newArray = copyArray(array) - newArray[method](...args) - return newArray -}) +const convertArrayMethod = (method, mutating = true) => { + const getArray = mutating ? toArrayCopy : toArray + const callMethod = mutating ? callMethodReturnArray : callMethodReturnResult + return convert((value, ...args) => callMethod(getArray(value), method, args)) +} export { convertArrayMethod } diff --git a/packages/immutadot/src/array/fill.js b/packages/immutadot/src/array/fill.js index 9763722c..a790957c 100644 --- a/packages/immutadot/src/array/fill.js +++ b/packages/immutadot/src/array/fill.js @@ -1,5 +1,4 @@ -import _fill from 'lodash/fp/fill' -import { convertLodashFp } from 'util/convert' +import { convertArrayMethod } from './convertArrayMethod' /** * Replaces by an array filled with value from start up to, but not including, end. @@ -16,5 +15,5 @@ import { convertLodashFp } from 'util/convert' * @since 0.3.0 * @flow */ -const fill = convertLodashFp(_fill) +const fill = convertArrayMethod('fill') export { fill } diff --git a/packages/immutadot/src/array/reverse.js b/packages/immutadot/src/array/reverse.js index 08e0e814..f1388165 100644 --- a/packages/immutadot/src/array/reverse.js +++ b/packages/immutadot/src/array/reverse.js @@ -1,5 +1,4 @@ -import _reverse from 'lodash/fp/reverse' -import { convertLodashFp } from 'util/convert' +import { convertArrayMethod } from './convertArrayMethod' /** * Replaces an array reversing the elements from the former array. @@ -13,5 +12,5 @@ import { convertLodashFp } from 'util/convert' * @since 0.3.0 * @flow */ -const reverse = convertLodashFp(_reverse) +const reverse = convertArrayMethod('reverse') export { reverse } diff --git a/packages/immutadot/src/array/slice.js b/packages/immutadot/src/array/slice.js index 70525fe7..b69229c6 100644 --- a/packages/immutadot/src/array/slice.js +++ b/packages/immutadot/src/array/slice.js @@ -1,5 +1,4 @@ -import _slice from 'lodash/slice' -import { convert } from 'core/convert' +import { convertArrayMethod } from './convertArrayMethod' /** * Replaces an array by a slice of the former array from start up to, but not including, end. @@ -15,5 +14,5 @@ import { convert } from 'core/convert' * @since 0.3.0 * @flow */ -const slice = convert(_slice) +const slice = convertArrayMethod('slice', false, true) export { slice } diff --git a/packages/immutadot/src/core/get.js b/packages/immutadot/src/core/get.js new file mode 100644 index 00000000..e9a10f8b --- /dev/null +++ b/packages/immutadot/src/core/get.js @@ -0,0 +1,23 @@ +import { isNil } from 'util/lang' +import { unsafeToPath } from './toPath' + +/** +* Gets the value at path of obj. +* @memberof core +* @param {*} obj The object. +* @param {string|Array} path The path of the property to get. +* @param {*} defaultValue The default value. +* @return {*} Returns the value or defaultValue. +* @example get({ nested: { prop: 'val' } }, 'nested.prop') // => 'val' +* @example get({ nested: { prop: 'val' } }, 'nested.unknown', 'default') // => 'default' +* @since 1.0.0 + */ +export function get(obj, path, defaultValue) { + function walkPath(curObj, remPath) { + if (remPath.length === 0) return curObj === undefined ? defaultValue : curObj + if (isNil(curObj)) return defaultValue + const [prop, ...pathRest] = remPath + return walkPath(curObj[prop], pathRest) + } + return walkPath(obj, unsafeToPath(path)) +} diff --git a/packages/immutadot/src/core/get.spec.js b/packages/immutadot/src/core/get.spec.js new file mode 100644 index 00000000..06585aa3 --- /dev/null +++ b/packages/immutadot/src/core/get.spec.js @@ -0,0 +1,28 @@ +/* eslint-env jest */ +import { get } from 'core' + +describe('Get', () => { + const obj = { + nested1: { prop: 'val' }, + nested2: { arr: [{ val: 'arrVal' }] }, + } + + it('should get a prop', () => { + expect(get(obj, 'nested1.prop')).toBe('val') + expect(get(obj, 'nested1.prop.length')).toBe(3) + expect(get(obj, 'nested2.arr.length')).toBe(1) + expect(get(obj, 'nested2.arr[0].val')).toBe('arrVal') + }) + + it('should return undefined for unexisting path', () => { + expect(get(obj, 'nested1.foo')).toBe(undefined) + expect(get(obj, 'nested3.val')).toBe(undefined) + expect(get(obj, 'nested2.arr[1].val')).toBe(undefined) + }) + + it('should return defaultValue for unexisting path', () => { + expect(get(obj, 'nested1.foo', 'defaultValue')).toBe('defaultValue') + expect(get(obj, 'nested3.val', 'defaultValue')).toBe('defaultValue') + expect(get(obj, 'nested2.arr[1].val', 'defaultValue')).toBe('defaultValue') + }) +}) diff --git a/packages/immutadot/src/core/index.js b/packages/immutadot/src/core/index.js index f2280550..090aec99 100644 --- a/packages/immutadot/src/core/index.js +++ b/packages/immutadot/src/core/index.js @@ -1,18 +1,12 @@ -import { convert } from './convert' -import { set } from './set' -import { toPath } from './toPath' -import { unset } from './unset' -import { update } from './update' - /** - * Core functions. - * @namespace core - * @since 1.0.0 - */ -export { - convert, - set, - toPath, - unset, - update, -} +* Core functions. +* @namespace core +* @since 1.0.0 +*/ + +export { convert } from './convert' +export { get } from './get' +export { set } from './set' +export { toPath } from './toPath' +export { unset } from './unset' +export { update } from './update' diff --git a/packages/immutadot/src/core/set.js b/packages/immutadot/src/core/set.js index 3ce140e8..328c39f7 100644 --- a/packages/immutadot/src/core/set.js +++ b/packages/immutadot/src/core/set.js @@ -3,7 +3,7 @@ import { apply } from './apply' const setOperation = (obj, prop, _, value) => { obj[prop] = value } /** - * Sets the value at path of object. + * Sets the value at path of obj. * @function * @memberof core * @param {*} obj The object to modify. diff --git a/packages/immutadot/src/core/toPath.js b/packages/immutadot/src/core/toPath.js index 3982789b..51bc7120 100644 --- a/packages/immutadot/src/core/toPath.js +++ b/packages/immutadot/src/core/toPath.js @@ -6,6 +6,7 @@ import { } from './parser.utils' import { + isNil, isSymbol, toString, } from 'util/lang' @@ -91,8 +92,7 @@ const isSliceIndexString = arg => isSliceIndex(arg ? Number(arg) : undefined) */ const allowingArrays = fn => arg => { if (Array.isArray(arg)) return arg.map(toKey) - - return fn(toString(arg)) + return fn(arg) } const emptyStringParser = str => str.length === 0 ? [] : null @@ -139,16 +139,7 @@ const pathSegmentEndedByBracketParser = map( ([beforeBracket, atBracket]) => [beforeBracket, ...stringToPath(atBracket)], ) -/** - * Converts str to a path represented as an array of keys. - * @function - * @param {string} str The string to convert - * @return {Array} The path represented as an array of keys - * @memberof core - * @private - * @since 1.0.0 - */ -const stringToPath = race([ +const applyParsers = race([ emptyStringParser, quotedBracketNotationParser, incompleteQuotedBracketNotationParser, @@ -160,6 +151,20 @@ const stringToPath = race([ str => [str], ]) +/** + * Converts arg to a path represented as an array of keys. + * @function + * @param {*} arg The value to convert + * @return {Array} The path represented as an array of keys + * @memberof core + * @private + * @since 1.0.0 + */ +const stringToPath = arg => { + if (isNil(arg)) return [] + return applyParsers(toString(arg)) +} + const MAX_CACHE_SIZE = 1000 const cache = new Map() diff --git a/packages/immutadot/src/core/toPath.spec.js b/packages/immutadot/src/core/toPath.spec.js index ed61edac..7813a01f 100644 --- a/packages/immutadot/src/core/toPath.spec.js +++ b/packages/immutadot/src/core/toPath.spec.js @@ -62,4 +62,9 @@ describe('ToPath', () => { '1,2', ]) }) + + it('should give empty path for nil values', () => { + expect(toPath(null)).toEqual([]) + expect(toPath(undefined)).toEqual([]) + }) }) diff --git a/packages/immutadot/src/math/add.js b/packages/immutadot/src/math/add.js index d4829ce8..65f4ddae 100644 --- a/packages/immutadot/src/math/add.js +++ b/packages/immutadot/src/math/add.js @@ -1,4 +1,3 @@ -import _add from 'lodash/add' import { convert } from 'core/convert' /** @@ -14,5 +13,5 @@ import { convert } from 'core/convert' * @since 0.1.7 * @flow */ -const add = convert(_add) +const add = convert((value, addition) => value + addition) export { add } diff --git a/packages/immutadot/src/math/divide.js b/packages/immutadot/src/math/divide.js index 42ed8606..7cc624f1 100644 --- a/packages/immutadot/src/math/divide.js +++ b/packages/immutadot/src/math/divide.js @@ -1,4 +1,3 @@ -import _divide from 'lodash/divide' import { convert } from 'core/convert' /** @@ -14,5 +13,5 @@ import { convert } from 'core/convert' * @since 0.3.0 * @flow */ -const divide = convert(_divide) +const divide = convert((value, divider) => value / divider) export { divide } diff --git a/packages/immutadot/src/math/multiply.js b/packages/immutadot/src/math/multiply.js index 7e8c16c8..00c33f25 100644 --- a/packages/immutadot/src/math/multiply.js +++ b/packages/immutadot/src/math/multiply.js @@ -1,4 +1,3 @@ -import _multiply from 'lodash/multiply' import { convert } from 'core/convert' /** @@ -14,5 +13,5 @@ import { convert } from 'core/convert' * @since 0.3.0 * @flow */ -const multiply = convert(_multiply) +const multiply = convert((value, multiplier) => value * multiplier) export { multiply } diff --git a/packages/immutadot/src/math/subtract.js b/packages/immutadot/src/math/subtract.js index 3af47921..5d6b9c56 100644 --- a/packages/immutadot/src/math/subtract.js +++ b/packages/immutadot/src/math/subtract.js @@ -1,4 +1,3 @@ -import _subtract from 'lodash/subtract' import { convert } from 'core/convert' /** @@ -14,5 +13,5 @@ import { convert } from 'core/convert' * @since 0.3.0 * @flow */ -const subtract = convert(_subtract) +const subtract = convert((value, subtraction) => value - subtraction) export { subtract } diff --git a/packages/immutadot/src/object/assign.js b/packages/immutadot/src/object/assign.js index c967789f..67fb502d 100644 --- a/packages/immutadot/src/object/assign.js +++ b/packages/immutadot/src/object/assign.js @@ -1,5 +1,4 @@ -import _assign from 'lodash/fp/assign' -import { convertLodashFp } from 'util/convert' +import { convert } from 'core/convert' /** * Replaces by an object assigning own enumerable string keyed properties of source objects to the destination object.
@@ -15,5 +14,5 @@ import { convertLodashFp } from 'util/convert' * @since 0.1.12 * @flow */ -const assign = convertLodashFp(_assign) +const assign = convert((obj, ...args) => Object.assign({ ...obj }, ...args)) export { assign } diff --git a/packages/immutadot/src/seq/ChainWrapper.js b/packages/immutadot/src/seq/ChainWrapper.js index bd27e5e3..db5315e0 100644 --- a/packages/immutadot/src/seq/ChainWrapper.js +++ b/packages/immutadot/src/seq/ChainWrapper.js @@ -5,11 +5,7 @@ import * as math from 'math' import * as object from 'object' import * as string from 'string' -import concat from 'lodash/concat' -import flow from 'lodash/flow' -import mapValues from 'lodash/mapValues' -import omit from 'lodash/omit' -import toPath from 'lodash/toPath' +import { unsafeToPath } from 'core/toPath' /** * Wrapper allowing to make sequences of immutadot functions calls on an object.
@@ -45,7 +41,7 @@ class ChainWrapper { * @since 0.1.11 */ _absolutePath(path) { - return concat(toPath(this._path), toPath(path)) + return unsafeToPath(this._path).concat(unsafeToPath(path)) } /** @@ -60,7 +56,7 @@ class ChainWrapper { return new ChainWrapper( this._wrapped, this._path, - concat(this._flow, object => fn(object, this._absolutePath(path), ...args)), + [...this._flow, object => fn(object, this._absolutePath(path), ...args)], ) } @@ -74,7 +70,7 @@ class ChainWrapper { if (this._commited === null) { this._commited = new ChainWrapper( - flow(this._flow)(this._wrapped), + this._flow.reduce((obj, fn) => fn(obj), this._wrapped), this._path, ) } @@ -122,21 +118,23 @@ class ChainWrapper { */ // Add namespaces functions to the ChainWrapper prototype -[ +const { convert, toPath, ...filteredCore } = core // eslint-disable-line no-unused-vars +const { set, unset, update, ...filteredObject } = object // eslint-disable-line no-unused-vars +const namespaces = [ array, - omit(core, ['convert', 'toPath']), + filteredCore, lang, math, - omit(object, ['set', 'unset', 'update']), + filteredObject, string, -].forEach(namespace => Object.assign( - ChainWrapper.prototype, - mapValues( - namespace, - fn => function(path, ...args) { - return this._call(fn, path, args) // eslint-disable-line no-invalid-this - }, - ), -)) +] +namespaces.forEach(namespace => { + for (const fnName in namespace) { + const fn = namespace[fnName] + ChainWrapper.prototype[fnName] = function(path, ...args) { + return this._call(fn, path, args) + } + } +}) export { ChainWrapper } diff --git a/packages/immutadot/src/string/replace.js b/packages/immutadot/src/string/replace.js index 49372f86..704e1554 100644 --- a/packages/immutadot/src/string/replace.js +++ b/packages/immutadot/src/string/replace.js @@ -1,4 +1,3 @@ -import _replace from 'lodash/replace' import { convert } from 'core/convert' /** @@ -15,5 +14,5 @@ import { convert } from 'core/convert' * @since 0.3.0 * @flow */ -const replace = convert(_replace) +const replace = convert((str, ...args) => str.replace(...args)) export { replace } diff --git a/packages/immutadot/src/util/UsingWrapper.js b/packages/immutadot/src/util/UsingWrapper.js index a8831373..6b8e39ba 100644 --- a/packages/immutadot/src/util/UsingWrapper.js +++ b/packages/immutadot/src/util/UsingWrapper.js @@ -5,14 +5,11 @@ import * as math from 'math' import * as object from 'object' import * as string from 'string' -import concat from 'lodash/concat' -import drop from 'lodash/drop' -import get from 'lodash/get' -import head from 'lodash/head' -import isSymbol from 'lodash/isSymbol' -import map from 'lodash/map' -import mapValues from 'lodash/mapValues' -import omit from 'lodash/omit' +import { isSymbol } from 'util/lang' + +const { get } = core + +const head = arr => arr[0] /** * Wrapper allowing to specify one or several paths to use as arguments for an immutadot function call.
@@ -52,37 +49,36 @@ class UsingWrapper { */ _call(fn, object, path, pArgs) { let callArgs = pArgs - const args = concat( - map(this._paths, usingPath => { - if (isSymbol(usingPath)) { - const arg = head(callArgs) - callArgs = drop(callArgs) - return arg - } - return get(object, usingPath) - }), - callArgs, - ) + const args = this._paths.map(usingPath => { + if (isSymbol(usingPath)) { + const arg = head(callArgs) + callArgs = callArgs.slice(1) + return arg + } + return get(object, usingPath) + }).concat(callArgs) return fn(object, path, ...args) } } // Add namespaces functions to the UsingWrapper prototype -[ +const { convert, unset, toPath, ...filteredCore } = core // eslint-disable-line no-unused-vars +const { get: _get, set, unset: _unset, update, ...filteredObject } = object // eslint-disable-line no-unused-vars +const namespaces = [ array, - omit(core, ['convert', 'unset', 'toPath']), + filteredCore, lang, math, - omit(object, ['set', 'unset', 'update']), + filteredObject, string, -].forEach(namespace => Object.assign( - UsingWrapper.prototype, - mapValues( - namespace, - fn => function(object, path, ...args) { - return this._call(fn, object, path, args) // eslint-disable-line no-invalid-this - }, - ), -)) +] +namespaces.forEach(namespace => { + for (const fnName in namespace) { + const fn = namespace[fnName] + UsingWrapper.prototype[fnName] = function(object, path, ...args) { + return this._call(fn, object, path, args) + } + } +}) export { UsingWrapper } diff --git a/packages/immutadot/src/util/index.js b/packages/immutadot/src/util/index.js index 45fddf40..d0089cfb 100644 --- a/packages/immutadot/src/util/index.js +++ b/packages/immutadot/src/util/index.js @@ -1,4 +1,3 @@ -import { convert } from './convert' import { protect } from './protect' import { using } from './using' @@ -8,7 +7,6 @@ import { using } from './using' * @since 0.1.13 */ export { - convert, using, protect, } diff --git a/packages/immutadot/src/util/lang.js b/packages/immutadot/src/util/lang.js index 77ea3ee5..2da24716 100644 --- a/packages/immutadot/src/util/lang.js +++ b/packages/immutadot/src/util/lang.js @@ -55,9 +55,20 @@ const length = arg => { */ const toString = arg => typeof arg === 'string' ? arg : `${arg}` +/** + * Tests whether arg is a object. + * @param {*} arg The value to test + * @return {boolean} True if arg is an Object, false otherwise + * @memberof util + * @private + * @since 1.0.0 + */ +const isObject = arg => arg instanceof Object + export { isNaturalInteger, isNil, + isObject, isSymbol, length, toString, diff --git a/packages/immutadot/src/util/lang.spec.js b/packages/immutadot/src/util/lang.spec.js index ceb8ae9b..7d027ae0 100644 --- a/packages/immutadot/src/util/lang.spec.js +++ b/packages/immutadot/src/util/lang.spec.js @@ -2,6 +2,7 @@ import { isNaturalInteger, isNil, + isObject, isSymbol, length, toString, @@ -87,4 +88,35 @@ describe('Lang utils', () => { expect(toString(666)).toBe('666') }) }) + + describe('isObject', () => { + it('should return true for object', () => { + expect(isObject({})).toBe(true) + }) + + it('should return true for array', () => { + expect(isObject([])).toBe(true) + }) + + it('should return true for function', () => { + const func = () => 1 + expect(isObject(func)).toBe(true) + }) + + it('should return true for string', () => { + expect(isObject('')).toBe(false) + }) + + it('should return true for number', () => { + expect(isObject(1)).toBe(false) + }) + + it('should return true for instance of wrappers', () => { + /* eslint-disable no-new-wrappers */ + expect(isObject(new Number(1))).toBe(true) + expect(isObject(new String(''))).toBe(true) + expect(isObject(new Boolean(true))).toBe(true) + /* eslint-enable no-new-wrappers */ + }) + }) }) diff --git a/packages/immutadot/src/util/protect.js b/packages/immutadot/src/util/protect.js index 62bad0d8..f85f6de6 100644 --- a/packages/immutadot/src/util/protect.js +++ b/packages/immutadot/src/util/protect.js @@ -1,8 +1,5 @@ import { chain } from 'seq/chain' -import concat from 'lodash/concat' -import get from 'lodash/get' -import isEmpty from 'lodash/isEmpty' -import isObject from 'lodash/isObject' +import { isObject } from 'util/lang' /** * Proxy handler to protect object from mutations. @@ -34,7 +31,7 @@ class ProtectHandler { get(target, property) { const reference = this._peek()[property] if (!isObject(reference)) return reference - return new Proxy(reference, new ProtectHandler(this.chainWrapperRef, concat(this.path, property))) + return new Proxy(reference, new ProtectHandler(this.chainWrapperRef, [...this.path, property])) } /** @@ -46,7 +43,7 @@ class ProtectHandler { * @since 0.3.0 */ set(target, property, value) { - this.chainWrapperRef.chainWrapper = this.chainWrapperRef.chainWrapper.set(concat(this.path, property), value) + this.chainWrapperRef.chainWrapper = this.chainWrapperRef.chainWrapper.set([...this.path, property], value) return true } @@ -58,7 +55,7 @@ class ProtectHandler { * @since 0.3.0 */ deleteProperty(target, property) { - this.chainWrapperRef.chainWrapper = this.chainWrapperRef.chainWrapper.unset(concat(this.path, property)) + this.chainWrapperRef.chainWrapper = this.chainWrapperRef.chainWrapper.unset([...this.path, property]) return true } @@ -70,7 +67,7 @@ class ProtectHandler { _peek() { let peeked this.chainWrapperRef.chainWrapper = this.chainWrapperRef.chainWrapper.peek(_peeked => { peeked = _peeked }) - return isEmpty(this.path) ? peeked : get(peeked, this.path) + return !this.path.length ? peeked : peeked[this.path] } }