immutable-assign
Advanced tools
Comparing version 2.0.9 to 2.1.0
@@ -38,2 +38,7 @@ declare namespace ImmutableAssign { | ||
// 4. We must remove the closing bracket of iassign(), and intellisense will be shown for "n" | ||
<TObj>( | ||
obj: TObj, | ||
setProp: setPropFunc<TObj>, | ||
option?: IIassignOption | ||
): TObj; | ||
@@ -48,5 +53,7 @@ <TObj, TProp, TContext>( | ||
<TObj>( | ||
<TObj, TProp, TContext>( | ||
obj: TObj, | ||
setProp: setPropFunc<TObj>, | ||
propPaths: (string | number)[], | ||
setProp: setPropFunc<TProp>, | ||
context?: TContext, | ||
option?: IIassignOption | ||
@@ -58,3 +65,5 @@ ): TObj; | ||
option: IIassignOption, | ||
getProp: getPropFunc<TObj, TProp, TContext>, | ||
getPropOrPropPath: | ||
| getPropFunc<TObj, TProp, TContext> | ||
| (string | number)[], | ||
setProp: setPropFunc<TProp>, | ||
@@ -61,0 +70,0 @@ context?: TContext, |
@@ -56,7 +56,8 @@ 'use strict'; | ||
function _iassign(obj, // Object to set property, it will not be modified. | ||
getPropOrSetProp, // Function to get property to be updated. Must be pure function. | ||
getPropOrSetPropOrPaths, // Function to get property to be updated. Must be pure function. | ||
setPropOrOption, // Function to set property. | ||
contextOrUndefined, // (Optional) Context to be used in getProp(). | ||
optionOrUndefined) { | ||
var getProp = getPropOrSetProp; | ||
var getProp = getPropOrSetPropOrPaths; | ||
var propPaths = undefined; | ||
var setProp = setPropOrOption; | ||
@@ -67,6 +68,11 @@ var context = contextOrUndefined; | ||
getProp = undefined; | ||
setProp = getPropOrSetProp; | ||
setProp = getPropOrSetPropOrPaths; | ||
context = undefined; | ||
option = setPropOrOption; | ||
} | ||
else { | ||
if (getProp instanceof Array) { | ||
propPaths = getProp; | ||
} | ||
} | ||
option = copyOption(undefined, option, iassign); | ||
@@ -88,20 +94,32 @@ if (deepFreeze && (option.freeze || option.freezeInput)) { | ||
else { | ||
// Check if getProp() is valid | ||
var value = getProp(obj, context); | ||
var newValue = undefined; | ||
if (option.ignoreIfNoChange) { | ||
newValue = setProp(value); | ||
if (newValue === value) { | ||
return obj; | ||
if (!propPaths) { | ||
// Check if getProp() is valid | ||
var value = getProp(obj, context); | ||
if (option.ignoreIfNoChange) { | ||
newValue = setProp(value); | ||
if (newValue === value) { | ||
return obj; | ||
} | ||
} | ||
var funcText = getProp.toString(); | ||
var arrowIndex = funcText.indexOf('=>'); | ||
if (arrowIndex <= -1) { | ||
var returnIndex = funcText.indexOf('return '); | ||
if (returnIndex <= -1) { | ||
throw new Error('getProp() function does not return a part of obj'); | ||
} | ||
} | ||
propPaths = getPropPath(getProp, obj, context, option); | ||
} | ||
var funcText = getProp.toString(); | ||
var arrowIndex = funcText.indexOf('=>'); | ||
if (arrowIndex <= -1) { | ||
var returnIndex = funcText.indexOf('return '); | ||
if (returnIndex <= -1) { | ||
throw new Error('getProp() function does not return a part of obj'); | ||
else { | ||
// Check if getProp() is valid | ||
var value = getPropByPaths(obj, propPaths); | ||
if (option.ignoreIfNoChange) { | ||
newValue = setProp(value); | ||
if (newValue === value) { | ||
return obj; | ||
} | ||
} | ||
} | ||
var propPaths = getPropPath(getProp, obj, context, option); | ||
if (!propPaths) { | ||
@@ -141,3 +159,3 @@ throw new Error('getProp() function does not return a part of obj'); | ||
isNaN(token.propName)) { | ||
throw new Error("Cannot handle " + token.propName + " when the property it point to is undefined, which require unsafe feature of eval."); | ||
throw new Error("Cannot handle " + token.propName + " when the property it point to is undefined, which require unsafe feature of e v a l."); | ||
} | ||
@@ -477,1 +495,10 @@ } | ||
}); | ||
function getPropByPaths(obj, paths) { | ||
paths = paths.slice(); | ||
var value = obj; | ||
while (paths.length > 0) { | ||
var path = paths.shift(); | ||
value = value[path]; | ||
} | ||
return value; | ||
} |
{ | ||
"name": "immutable-assign", | ||
"version": "2.0.9", | ||
"version": "2.1.0", | ||
"description": "Lightweight immutable helper that allows you to continue working with Plain JavaScript Objects", | ||
@@ -13,3 +13,3 @@ "main": "deploy/iassign.js", | ||
"test-karma-mac": "node_modules/.bin/karma start --browsers Safari,Chrome", | ||
"test-karma-mac-no-proxy": "NO_PROXY='true' yarn run test-karma-mac", | ||
"test-karma-mac-no-proxy": "NO_PROXY='true' npm run test-karma-mac", | ||
"debug": "node --inspect --inspect-brk node_modules/jasmine/bin/jasmine.js", | ||
@@ -67,2 +67,3 @@ "build": "gulp", | ||
"merge2": "^1.2.2", | ||
"minimatch": "^3.0.4", | ||
"seamless-immutable": "^7.1.3", | ||
@@ -73,2 +74,2 @@ "timm": "^1.6.1", | ||
} | ||
} | ||
} |
159
README.md
@@ -16,3 +16,3 @@ # immutable-assign (iassign.js) | ||
This library is trying to solve following problems: | ||
This library is trying to solve the following problems: | ||
@@ -22,3 +22,3 @@ * Most immutable JavaScript libraries try to encapsulate the data and provide proprietary APIs to work with the data. They are more verbose than normal JavaScript syntax. E.g., map1.get('b') vs map1.b, nested2.getIn(['a', 'b', 'd']) vs nested2.a.b.d, etc. | ||
* Most immutable libraries leak themselves throughout your entire application (including view components), however, they should have been encapsulated at the place where updates happen (e.g., Redux reducers). This is also a pain when you need to change to another immutable library that has its own APIs. | ||
* [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) address some of above issues when reading the properties, but still use verbose APIs to write properties. | ||
* [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) address some of the above issues when reading the properties, but still use verbose APIs to write properties. | ||
* [Immutability Helpers](https://facebook.github.io/react/docs/update.html) allows us to work with POJO, but it has still introduced some magic keywords, such as $set, $push, etc. | ||
@@ -73,14 +73,14 @@ * In addition, we lost TypeScript type checking. E.g., when calling nested2.getIn(["a", "b", "c"]), TypeScript won't be able to warn me if I changed property "c" to "d". | ||
### Example 1: Update root object | ||
### Example 1: Update 1st level object properties | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
// Deep freeze both input and output, can be used in development to make sure they don't change. | ||
iassign.freeze = true; | ||
iassign.setOption({ freeze: true }); | ||
var map1 = { a:1, b:2, c:3 }; | ||
const map1 = { a:1, b:2, c:3 }; | ||
// 1: Calling iassign() to update map1.b, using overload 2 | ||
var map2 = iassign( | ||
// 1: Calling iassign() to update map1.b, using overload 1 | ||
const map2 = iassign( | ||
map1, | ||
@@ -97,12 +97,12 @@ function (m) { m.b = 50; return m; } | ||
### Example 2: Update root list/array | ||
### Example 2: Update 1st level list/array elements | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var list1 = [1, 2]; | ||
const list1 = [1, 2]; | ||
// 2.1: Calling iassign() to push items to list1, using overload 2 | ||
var list2 = iassign( | ||
// 2.1: Calling iassign() to push items to list1, using overload 1 | ||
const list2 = iassign( | ||
list1, | ||
@@ -116,4 +116,4 @@ function (l) { l.push(3, 4, 5); return l; } | ||
// 2.2: Calling iassign() to unshift item to list2, using overload 2 | ||
var list3 = iassign( | ||
// 2.2: Calling iassign() to unshift item to list2, using overload 1 | ||
const list3 = iassign( | ||
list2, | ||
@@ -127,4 +127,4 @@ function (l) { l.unshift(0); return l; } | ||
// 2.3, Calling iassign() to concat list1, list2 and list3, using overload 2 | ||
var list4 = iassign( | ||
// 2.3, Calling iassign() to concat list1, list2 and list3, using overload 1 | ||
const list4 = iassign( | ||
list1, | ||
@@ -138,4 +138,4 @@ function (l) { return l.concat(list2, list3); } | ||
// 2.4, Calling iassign() to concat sort list4, using overload 2 | ||
var list5 = iassign( | ||
// 2.4, Calling iassign() to concat sort list4, using overload 1 | ||
const list5 = iassign( | ||
list4, | ||
@@ -152,12 +152,12 @@ function (l) { return l.sort(); } | ||
### Example 3: Update nested object | ||
### Example 3: Update nested level object properties | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var nested1 = { a:{ b:{ c:[3, 4, 5] } } }; | ||
const nested1 = { a:{ b:{ c:[3, 4, 5] } } }; | ||
// 3.1: Calling iassign() to assign d to nested1.a.b | ||
var nested2 = iassign( | ||
const nested2 = iassign( | ||
nested1, | ||
@@ -173,3 +173,3 @@ function (n) { return n.a.b; }, | ||
// 3.2: Calling iassign() to increment nested2.a.b.d | ||
var nested3 = iassign( | ||
const nested3 = iassign( | ||
nested2, | ||
@@ -185,3 +185,3 @@ function (n) { return n.a.b.d; }, | ||
// 3.3: Calling iassign() to push item to nested3.a.b.c | ||
var nested4 = iassign( | ||
const nested4 = iassign( | ||
nested3, | ||
@@ -202,10 +202,10 @@ function (n) { return n.a.b.c; }, | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
var _ = require("lodash"); | ||
const iassign = require("immutable-assign"); | ||
const _ = require("lodash"); | ||
var nested1 = { a: { b: { c: [1, 2, 3] } } }; | ||
const nested1 = { a: { b: { c: [1, 2, 3] } } }; | ||
// 4.1: Calling iassign() and _.map() to increment to every item in "c" array | ||
var nested2 = iassign( | ||
const nested2 = iassign( | ||
nested1, | ||
@@ -223,3 +223,3 @@ function (n) { return n.a.b.c; }, | ||
// 4.2: Calling iassign() and _.flatMap() | ||
var nested3 = iassign( | ||
const nested3 = iassign( | ||
nested2, | ||
@@ -242,8 +242,8 @@ function (n) { return n.a.b.c; }, | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }; | ||
const o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }; | ||
// 5: Calling iassign() to increment o1.a.b.c[0][0].d | ||
var o2 = iassign( | ||
const o2 = iassign( | ||
o1, | ||
@@ -260,8 +260,8 @@ function (o) { return o.a.b.c[0][0]; }, | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }; | ||
const o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }; | ||
// 6: Calling iassign() to push new item to o1.a.b.c[1] | ||
var o2 = iassign( | ||
const o2 = iassign( | ||
o1, | ||
@@ -278,10 +278,10 @@ function (o) { return o.a.b.c[1]; }, | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var o1 = { a: { b: { c: [{ d: 11, e: 12 }, { d: 21, e: 22 }] } } }; | ||
const o1 = { a: { b: { c: [{ d: 11, e: 12 }, { d: 21, e: 22 }] } } }; | ||
// 7: Calling iassign() to push increment to o1.a.b.c[0].d | ||
var external = { a: 0 }; | ||
const external = { a: 0 }; | ||
var o2 = iassign( | ||
const o2 = iassign( | ||
o1, | ||
@@ -299,9 +299,9 @@ function (o, ctx) { return o.a.b.c[ctx.external.a]; }, | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var nested1 = { a: { b: { c: [3, 4, 5] } } }; | ||
const nested1 = { a: { b: { c: [3, 4, 5] } } }; | ||
// 8.1: Calling iassign() to assign d to nested1.a.b | ||
var iassignFp = iassign.fp(undefined) | ||
const iassignFp = iassign.fp(undefined) | ||
(function (n) { return n.a.b; }) | ||
@@ -311,3 +311,3 @@ (function (b) { b.d = 6; return b; }) | ||
var nested2 = iassignFp(nested1); | ||
const nested2 = iassignFp(nested1); | ||
@@ -322,3 +322,3 @@ // nested2 = { a: { b: { c: [3, 4, 5], d: 6 } } }; | ||
(undefined); | ||
var nested3 = iassignFp(nested2); | ||
const nested3 = iassignFp(nested2); | ||
@@ -333,3 +333,3 @@ // nested3 = { a: { b: { c: [3, 4, 5], d: 7 } } }; | ||
(undefined); | ||
var nested4 = iassignFp(nested3); | ||
const nested4 = iassignFp(nested3); | ||
@@ -344,3 +344,3 @@ // nested4 = { a: { b: { c: [3, 4, 5, 6], d: 7 } } }; | ||
({i: 1}); | ||
var nested5 = iassignFp(nested4); | ||
const nested5 = iassignFp(nested4); | ||
@@ -352,8 +352,8 @@ // nested5 = { a: { b: { c: [3, 104, 5, 6], d: 7 } } }; | ||
### Example 9: Support ES6 Map | ||
### Example 9: Support the ES6 Map | ||
```javascript | ||
var iassign = require("immutable-assign"); | ||
const iassign = require("immutable-assign"); | ||
var map1 = new Map(); | ||
const map1 = new Map(); | ||
map1.set("a", "value a"); | ||
@@ -371,3 +371,3 @@ | ||
var map2 = iassign( | ||
const map2 = iassign( | ||
map1, | ||
@@ -383,2 +383,21 @@ m => { m.set(1, 'first'); return m; } | ||
### Example 10: Update nested level object properties using property paths (overload 3) | ||
```javascript | ||
const iassign = require("immutable-assign"); | ||
const o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} } } }; | ||
const o2 = iassign( | ||
o1, | ||
['a', 'b', 'c', 0, '0'], | ||
(ci: any) => { | ||
ci.d++; | ||
return ci; | ||
}); | ||
// o2 = { a: { b: { c: [[{ d: 12, e: 12 }], [{ d: 21, e: 22 }]], c2: {} } } }; | ||
// o2 !== o1 | ||
``` | ||
<br /> | ||
@@ -388,7 +407,13 @@ | ||
```javascript | ||
```typescript | ||
// Return a new POJO object with property updated. | ||
// function overload 1: | ||
// function overload 1: you can skip getProp() if you trying to update the 1st level object properties, refer to example 1 and 2 | ||
iassign = function<TObj>( | ||
obj: TObj, // POJO object to be getting the property from, it will not be modified. | ||
setProp: setPropFunc<TObj>, // Function to set the property. | ||
option?: IIassignOption): TObj; // (Optional) Options | ||
// function overload 2: use getProp() to get prop paths | ||
iassign = function<TObj, TProp, TContext>( | ||
@@ -401,5 +426,6 @@ obj: TObj, // POJO object to be getting the property from, it will not be modified. | ||
// function overload 2: you can skip getProp() if you trying to update the root object, refer to example 1 and 2 | ||
iassign = function<TObj>( | ||
// function overload 3: pass in known property paths (array) | ||
iassign = function<TObj, TProp, TContext>( | ||
obj: TObj, // POJO object to be getting the property from, it will not be modified. | ||
propPaths: (string | number)[], // paths to the property that needs to be updated. | ||
setProp: setPropFunc<TObj>, // Function to set the property. | ||
@@ -411,3 +437,3 @@ option?: IIassignOption): TObj; // (Optional) Options | ||
option: IIassignOption, | ||
getProp: getPropFunc<TObj, TProp, TContext>, | ||
getPropOrPropPaths: getPropFunc<TObj, TProp, TContext> | (string | number)[], | ||
setProp: setPropFunc<TProp>, | ||
@@ -417,7 +443,7 @@ context?: TContext, | ||
// In ES6, you cannot set property on imported module directly, because they are default | ||
// to readonly, in this case you need to use this method. | ||
// In ES6, you cannot set any property on imported modules directly, because they default | ||
// to readonly, in this case, you need to use this method. | ||
iassign.setOption(option: IIassignOption): void; | ||
// Options, can be applied globally or individually | ||
// Options: can be applied globally or individually | ||
interface IIassignOption { | ||
@@ -437,3 +463,3 @@ freeze?: boolean; // Deep freeze both input and output | ||
// Return the same object if setProp() returns its parameter (i.e., reference pointer not changed). | ||
// Return the same object if setProp() returns the input with no change. | ||
ignoreIfNoChange?: boolean; | ||
@@ -450,4 +476,5 @@ } | ||
* 2.1.0 - Added function overload 3 to pass in known property paths (array). Refer to [example 10](#example-10-update-nested-level-object-properties-using-property-paths-overload-3) | ||
* 2.0.8 - Fixed bug for undefined properties. | ||
* 2.0.4 - Replaced the proxy-polyfill with Object.defineProperty(), which has much better browser support. | ||
* 2.0.4 - Replaced the proxy-polyfill with Object.defineProperty(), which has a much better browser support. | ||
* 2.0.1 - Minor bug fixes. | ||
@@ -458,14 +485,14 @@ * 2.0.0 - | ||
* 1.0.36 - [Supports ES6 Map and Set](https://github.com/engineforce/ImmutableAssign/issues/12). Refer to [example 9](https://github.com/engineforce/ImmutableAssign#example-9-support-es6-map) | ||
* 1.0.36 - [Supports ES6 Map and Set](https://github.com/engineforce/ImmutableAssign/issues/12). Refer to [example 9](#example-9-support-the-es6-map) | ||
* 1.0.35 - Supports ES6 default export. | ||
* 1.0.31 - | ||
* Added ignoreIfNoChange option, which cause iassign to return the same object if setProp() returns its parameter (i.e., reference pointer not changed). | ||
* Added setOption() function to allow you set the iassign options globally in ES6. | ||
* Added ignoreIfNoChange option, which causes iassign to return the same object if setProp() returns its parameter (i.e., reference pointer not changed). | ||
* Added setOption() function to allow you to set the iassign options globally in ES6. | ||
* 1.0.30 - [Support classes](https://github.com/engineforce/ImmutableAssign/issues/4) | ||
* 1.0.29 - Supported ES6 [Arrow Functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) | ||
* 1.0.27 - Added iassign.fp() that support [currying](https://www.sitepoint.com/currying-in-functional-javascript), refer to [example 8](#example-8-update-nested-structures-using-iassignfp-and-currying) | ||
* 1.0.27 - Added iassign.fp() that support [currying](https://www.sitepoint.com/currying-in-functional-javascript), refer to [example 8](#example-8-update-nested-object-using-iassignfp-and-currying) | ||
* 1.0.26 - Works with webpack, please refer to [ImmutableAssignTest](https://github.com/engineforce/ImmutableAssignTest) | ||
* 1.0.23 - Greatly improved performance. | ||
* 1.0.21 - Added new function overload to skip getProp() if you trying to update the root object, refer to [example 1](#example-1-update-object) and [example 2](#example-2-update-listarray) | ||
* 1.0.21 - Added function overload 1 to skip getProp() if you trying to update the 1st level object properties, refer to [example 1](#example-1-update-1st-level-object-properties) and [example 2](#example-2-update-1st-level-listarray-elements) | ||
* 1.0.20 - Added Travis-CI, Coveralls (coverage) and SauceLabs (browsers' tests) | ||
@@ -472,0 +499,0 @@ * 1.0.19 - Added TypeScript types to package.json |
@@ -10,3 +10,3 @@ { | ||
"removeComments": false, | ||
"noUnusedLocals": true, | ||
// "noUnusedLocals": true, | ||
"jsx": "preserve" /*, | ||
@@ -21,2 +21,2 @@ "declaration": true */ | ||
] | ||
} | ||
} |
7506
482
1415530
30