immutable-assign
Advanced tools
Comparing version 1.0.20 to 1.0.21
@@ -5,20 +5,38 @@ | ||
interface IIassignOption { | ||
freeze: boolean; // Deep freeze both input and output | ||
freezeInput: boolean; // Deep freeze input | ||
freezeOutput: boolean; // Deep freeze output | ||
disableAllCheck: boolean; | ||
disableHasReturnCheck: boolean; | ||
freeze?: boolean; // Deep freeze both input and output | ||
freezeInput?: boolean; // Deep freeze input | ||
freezeOutput?: boolean; // Deep freeze output | ||
disableAllCheck?: boolean; | ||
disableHasReturnCheck?: boolean; | ||
// Disable validation for extra statements in the getProp() function, | ||
// which is needed when running the coverage, e.g., istanbul.js does add | ||
// instrument statements in our getProp() function, which can be safely ignored. | ||
disableExtraStatementCheck: boolean; | ||
disableExtraStatementCheck?: boolean; | ||
} | ||
type getPropFunc<TObj, TProp, TContext> = (obj: TObj, context: TContext) => TProp; | ||
type setPropFunc<TProp> = (prop: TProp) => TProp; | ||
interface IIassign extends IIassignOption { | ||
// Intellisense for the TObj parameter in getProp will only work if we remove the auto added closing bracket of iassign, | ||
// and manually add the closing bracket at last. i.e., | ||
// | ||
// 1. Type iassign( in the editor | ||
// 2. Most editor will auto complete with closing bracket, e.g., iassign() | ||
// 3. If we continue to type without removing the closing bracket, e.g., iassign(nested, (n) => n.), | ||
// editor such as VS Code will not show any intellisense for "n" | ||
// 4. We must remove the closing bracket of iassign(), and intellisense will be shown for "n" | ||
<TObj, TProp, TContext>( | ||
obj: TObj, | ||
getProp: (obj: TObj, context: TContext) => TProp, | ||
setProp: (prop: TProp) => TProp, | ||
getProp: getPropFunc<TObj, TProp, TContext>, | ||
setProp: setPropFunc<TProp>, | ||
context?: TContext, | ||
option?: IIassignOption): TObj; | ||
<TObj>( | ||
obj: TObj, | ||
setProp: setPropFunc<TObj>, | ||
option?: IIassignOption): TObj; | ||
} | ||
@@ -25,0 +43,0 @@ } |
@@ -32,6 +32,16 @@ "use strict"; | ||
function _iassign(obj, // Object to set property, it will not be modified. | ||
getProp, // Function to get property to be updated. Must be pure function. | ||
setProp, // Function to set property. | ||
context, // (Optional) Context to be used in getProp(). | ||
option) { | ||
getPropOrSetProp, // 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 setProp = setPropOrOption; | ||
var context = contextOrUndefined; | ||
var option = optionOrUndefined; | ||
if (typeof setPropOrOption !== "function") { | ||
getProp = undefined; | ||
setProp = getPropOrSetProp; | ||
context = undefined; | ||
option = setPropOrOption; | ||
} | ||
if (option) { | ||
@@ -46,89 +56,95 @@ option = extend({}, iassign, option); | ||
} | ||
// Check if getProp() is valid | ||
var value = getProp(obj, context); | ||
var getPropFuncInfo = parseGetPropFuncInfo(getProp, option); | ||
var accessorText = getPropFuncInfo.accessorText; | ||
var propIndex = 0; | ||
var propValue = undefined; | ||
while (accessorText) { | ||
var openBracketIndex = accessorText.indexOf("["); | ||
var closeBracketIndex = accessorText.indexOf("]"); | ||
var dotIndex = accessorText.indexOf("."); | ||
var propName = ""; | ||
var propNameSource = ePropNameSource.none; | ||
// if (dotIndex == 0) { | ||
// accessorText = accessorText.substr(dotIndex + 1); | ||
// continue; | ||
// } | ||
if (openBracketIndex > -1 && closeBracketIndex <= -1) { | ||
throw new Error("Found open bracket but not close bracket."); | ||
} | ||
if (openBracketIndex <= -1 && closeBracketIndex > -1) { | ||
throw new Error("Found close bracket but not open bracket."); | ||
} | ||
if (dotIndex > -1 && (dotIndex < openBracketIndex || openBracketIndex <= -1)) { | ||
propName = accessorText.substr(0, dotIndex); | ||
accessorText = accessorText.substr(dotIndex + 1); | ||
propNameSource = ePropNameSource.beforeDot; | ||
} | ||
else if (openBracketIndex > -1 && (openBracketIndex < dotIndex || dotIndex <= -1)) { | ||
if (openBracketIndex > 0) { | ||
propName = accessorText.substr(0, openBracketIndex); | ||
accessorText = accessorText.substr(openBracketIndex); | ||
propNameSource = ePropNameSource.beforeBracket; | ||
if (!getProp) { | ||
obj = quickCopy(obj); | ||
obj = setProp(obj); | ||
} | ||
else { | ||
// Check if getProp() is valid | ||
var value = getProp(obj, context); | ||
var getPropFuncInfo = parseGetPropFuncInfo(getProp, option); | ||
var accessorText = getPropFuncInfo.accessorText; | ||
var propIndex = 0; | ||
var propValue = undefined; | ||
while (accessorText) { | ||
var openBracketIndex = accessorText.indexOf("["); | ||
var closeBracketIndex = accessorText.indexOf("]"); | ||
var dotIndex = accessorText.indexOf("."); | ||
var propName = ""; | ||
var propNameSource = ePropNameSource.none; | ||
// if (dotIndex == 0) { | ||
// accessorText = accessorText.substr(dotIndex + 1); | ||
// continue; | ||
// } | ||
if (openBracketIndex > -1 && closeBracketIndex <= -1) { | ||
throw new Error("Found open bracket but not close bracket."); | ||
} | ||
if (openBracketIndex <= -1 && closeBracketIndex > -1) { | ||
throw new Error("Found close bracket but not open bracket."); | ||
} | ||
if (dotIndex > -1 && (dotIndex < openBracketIndex || openBracketIndex <= -1)) { | ||
propName = accessorText.substr(0, dotIndex); | ||
accessorText = accessorText.substr(dotIndex + 1); | ||
propNameSource = ePropNameSource.beforeDot; | ||
} | ||
else if (openBracketIndex > -1 && (openBracketIndex < dotIndex || dotIndex <= -1)) { | ||
if (openBracketIndex > 0) { | ||
propName = accessorText.substr(0, openBracketIndex); | ||
accessorText = accessorText.substr(openBracketIndex); | ||
propNameSource = ePropNameSource.beforeBracket; | ||
} | ||
else { | ||
propName = accessorText.substr(openBracketIndex + 1, closeBracketIndex - 1); | ||
accessorText = accessorText.substr(closeBracketIndex + 1); | ||
propNameSource = ePropNameSource.inBracket; | ||
} | ||
} | ||
else { | ||
propName = accessorText.substr(openBracketIndex + 1, closeBracketIndex - 1); | ||
accessorText = accessorText.substr(closeBracketIndex + 1); | ||
propNameSource = ePropNameSource.inBracket; | ||
propName = accessorText; | ||
accessorText = ""; | ||
propNameSource = ePropNameSource.last; | ||
} | ||
} | ||
else { | ||
propName = accessorText; | ||
accessorText = ""; | ||
propNameSource = ePropNameSource.last; | ||
} | ||
propName = propName.trim(); | ||
if (propName == "") { | ||
continue; | ||
} | ||
//console.log(propName); | ||
if (propIndex <= 0) { | ||
propValue = quickCopy(obj); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
propName = propName.trim(); | ||
if (propName == "") { | ||
continue; | ||
} | ||
obj = propValue; | ||
} | ||
else { | ||
var prevPropValue = propValue; | ||
if (propNameSource == ePropNameSource.inBracket && isNaN(propName)) { | ||
if (propName[0] == "#") { | ||
var quotedPropName = getPropFuncInfo.quotedTextInfos[propName]; | ||
if (!quotedPropName) { | ||
throw new Error("Cannot find quoted text for " + quotedPropName); | ||
} | ||
propName = eval(quotedPropName); | ||
//console.log(propName); | ||
if (propIndex <= 0) { | ||
propValue = quickCopy(obj); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
else { | ||
var statement = "'use strict';\n"; | ||
if (getPropFuncInfo.objParameterName) { | ||
statement += "var " + getPropFuncInfo.objParameterName + " = arguments[1];\n"; | ||
obj = propValue; | ||
} | ||
else { | ||
var prevPropValue = propValue; | ||
if (propNameSource == ePropNameSource.inBracket && isNaN(propName)) { | ||
if (propName[0] == "#") { | ||
var quotedPropName = getPropFuncInfo.quotedTextInfos[propName]; | ||
if (!quotedPropName) { | ||
throw new Error("Cannot find quoted text for " + quotedPropName); | ||
} | ||
propName = eval(quotedPropName); | ||
} | ||
if (getPropFuncInfo.cxtParameterName) { | ||
statement += "var " + getPropFuncInfo.cxtParameterName + " = arguments[2];\n"; | ||
else { | ||
var statement = "'use strict';\n"; | ||
if (getPropFuncInfo.objParameterName) { | ||
statement += "var " + getPropFuncInfo.objParameterName + " = arguments[1];\n"; | ||
} | ||
if (getPropFuncInfo.cxtParameterName) { | ||
statement += "var " + getPropFuncInfo.cxtParameterName + " = arguments[2];\n"; | ||
} | ||
statement += "" + propName; | ||
propName = evalStatement(statement, obj, context); | ||
} | ||
statement += "" + propName; | ||
propName = evalStatement(statement, obj, context); | ||
} | ||
propValue = propValue[propName]; | ||
propValue = quickCopy(propValue); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
prevPropValue[propName] = propValue; | ||
} | ||
propValue = propValue[propName]; | ||
propValue = quickCopy(propValue); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
prevPropValue[propName] = propValue; | ||
//console.log(propValue); | ||
propIndex++; | ||
} | ||
//console.log(propValue); | ||
propIndex++; | ||
} | ||
@@ -135,0 +151,0 @@ if (deepFreeze && (option.freeze || option.freezeOutput)) { |
@@ -13,2 +13,25 @@ // Karma configuration | ||
// // } | ||
// // sl_ie9: { // Deep freeze is not working | ||
// // base: 'SauceLabs', | ||
// // browserName: 'internet explorer', | ||
// // platform: 'Windows 7', | ||
// // version: '9' | ||
// // }, | ||
// sl_ios_iphone: { | ||
// base: 'SauceLabs', | ||
// browserName: 'iphone', | ||
// platform: 'iOS 10', | ||
// }, | ||
// sl_android_chrome: { // Deep freeze is not working | ||
// base: 'SauceLabs', | ||
// browserName: 'android', | ||
// platform: 'Android 4', | ||
// version: "4.4" | ||
// }, | ||
// sl_android_chrome: { // Deep freeze is not working | ||
// base: 'SauceLabs', | ||
// browserName: 'android', | ||
// platform: 'Android 5', | ||
// version: "5" | ||
// } | ||
// } | ||
@@ -21,4 +44,10 @@ | ||
platform: 'Windows 10', | ||
version: 'latest' | ||
version: '11' | ||
}, | ||
sl_ie10: { | ||
base: 'SauceLabs', | ||
browserName: 'internet explorer', | ||
platform: 'Windows 8', | ||
version: '10' | ||
}, | ||
sl_edge: { | ||
@@ -42,3 +71,3 @@ base: 'SauceLabs', | ||
}, | ||
sl_mac_chrome: { | ||
@@ -63,3 +92,3 @@ base: 'SauceLabs', | ||
sl_ios_safari: { | ||
sl_ios_10_iphone: { | ||
base: 'SauceLabs', | ||
@@ -69,2 +98,8 @@ browserName: 'iphone', | ||
}, | ||
sl_android_5: { | ||
base: 'SauceLabs', | ||
browserName: 'android', | ||
platform: 'Android 5', | ||
varsion: "5" | ||
}, | ||
} | ||
@@ -151,4 +186,5 @@ config.set({ | ||
captureTimeout: 300000, | ||
captureTimeout: 120000, | ||
browserNoActivityTimeout: 300000, | ||
retryLimit: 5, | ||
@@ -155,0 +191,0 @@ // Concurrency level |
{ | ||
"name": "immutable-assign", | ||
"version": "1.0.20", | ||
"version": "1.0.21", | ||
"description": "Lightweight immutable helper that allows you to continue working with Plain JavaScript Objects", | ||
@@ -5,0 +5,0 @@ "main": "src/iassign.js", |
@@ -37,3 +37,6 @@ # immutable-assign (iassign.js) | ||
```javascript | ||
// Return a new POJO object with property updated. | ||
// function overload 1: | ||
function iassign<TObj, TProp, TContext>( | ||
@@ -45,8 +48,15 @@ obj: TObj, // POJO object to be getting the property from, it will not be modified. | ||
option?: IIassignOption): TObj; // (Optional) Options | ||
// function overload 2: you can skip getProp() if you trying to update the root object, refer to example 1 and 2 | ||
function iassign<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 | ||
// Options, can be applied globally or individually | ||
interface IIassignOption { | ||
freeze: boolean; // Deep freeze both input and output | ||
freezeInput: boolean; // Deep freeze input | ||
freezeOutput: boolean; // Deep freeze output | ||
freeze?: boolean; // Deep freeze both input and output | ||
freezeInput?: boolean; // Deep freeze input | ||
freezeOutput?: boolean; // Deep freeze output | ||
@@ -56,3 +66,3 @@ // Disable validation for extra statements in the getProp() function, | ||
// instrument statements in our getProp() function, which can be safely ignored. | ||
disableExtraStatementCheck: boolean; | ||
disableExtraStatementCheck?: boolean; | ||
} | ||
@@ -71,6 +81,5 @@ ``` | ||
// 1: Calling iassign() to update map1.b | ||
// 1: Calling iassign() to update map1.b, using overload 2 | ||
var map2 = iassign( | ||
map1, | ||
function (m) { return m; }, | ||
function (m) { m.b = 50; return m; } | ||
@@ -92,6 +101,5 @@ ); | ||
// 2.1: Calling iassign() to push items to list1 | ||
// 2.1: Calling iassign() to push items to list1, using overload 2 | ||
var list2 = iassign( | ||
list1, | ||
function (l) { return l; }, | ||
function (l) { l.push(3, 4, 5); return l; } | ||
@@ -104,6 +112,5 @@ ); | ||
// 2.2: Calling iassign() to unshift item to list2 | ||
// 2.2: Calling iassign() to unshift item to list2, using overload 2 | ||
var list3 = iassign( | ||
list2, | ||
function (l) { return l; }, | ||
function (l) { l.unshift(0); return l; } | ||
@@ -116,6 +123,5 @@ ); | ||
// 2.3, Calling iassign() to concat list1, list2 and list3 | ||
// 2.3, Calling iassign() to concat list1, list2 and list3, using overload 2 | ||
var list4 = iassign( | ||
list1, | ||
function (l) { return l; }, | ||
function (l) { return l.concat(list2, list3); } | ||
@@ -128,6 +134,5 @@ ); | ||
// 2.4, Calling iassign() to concat sort list4 | ||
// 2.4, Calling iassign() to concat sort list4, using overload 2 | ||
var list5 = iassign( | ||
list4, | ||
function (l) { return l; }, | ||
function (l) { return l.sort(); } | ||
@@ -349,3 +354,2 @@ ); | ||
##Constraints | ||
@@ -358,2 +362,3 @@ | ||
* 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.20 - Added Travis-CI, Coveralls (coverage) and SauceLabs (browsers' tests) | ||
@@ -360,0 +365,0 @@ * 1.0.19 - Added TypeScript types to package.json |
@@ -408,3 +408,140 @@ "use strict"; | ||
}); | ||
it("Example 1: update object", function () { | ||
//var 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; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1: Calling iassign() to update map1.b | ||
var map2 = iassign(map1, function (m) { m.b = 50; return m; }); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
}); | ||
it("Example 1b: update object with option", function () { | ||
//var 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; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1: Calling iassign() to update map1.b | ||
var map2 = iassign(map1, function (m) { m.b = 50; return m; }); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
expect(function () { map2.a = 3; }).toThrow(); | ||
var map3 = iassign(map2, function (m) { m.c = 60; return m; }, { freeze: false }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map3).toEqual({ a: 1, b: 50, c: 60 }); | ||
expect(map3).not.toBe(map2); | ||
expect(function () { map3.a = 3; }).not.toThrow(); | ||
}); | ||
it("Example 1c: update object that pass undefined to getProp()", function () { | ||
//var 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; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1c: Calling iassign() to update map1.b | ||
var map2 = iassign(map1, undefined, function (m) { m.b = 50; return m; }); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
}); | ||
it("Example 1d: update object with option that pass undefined to getProp()", function () { | ||
//var 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; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1: Calling iassign() to update map1.b | ||
var map2 = iassign(map1, undefined, function (m) { m.b = 50; return m; }); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
expect(function () { map2.a = 3; }).toThrow(); | ||
var map3 = iassign(map2, undefined, function (m) { m.c = 60; return m; }, undefined, { freeze: false }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map3).toEqual({ a: 1, b: 50, c: 60 }); | ||
expect(map3).not.toBe(map2); | ||
expect(function () { map3.a = 3; }).not.toThrow(); | ||
}); | ||
it("Example 2: update list/array", function () { | ||
//var 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; | ||
var list1 = [1, 2]; | ||
// 2.1: Calling iassign() to push items to list1 | ||
var list2 = iassign(list1, function (l) { l.push(3, 4, 5); return l; }); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list2).not.toBe(list1); | ||
// 2.2: Calling iassign() to unshift item to list2 | ||
var list3 = iassign(list2, function (l) { l.unshift(0); return l; }); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list3).not.toBe(list2); | ||
// 2.3, Calling iassign() to concat list1, list2 and list3 | ||
var list4 = iassign(list1, function (l) { return l.concat(list2, list3); }); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list4).not.toBe(list1); | ||
expect(list4).not.toBe(list2); | ||
expect(list4).not.toBe(list3); | ||
// 2.4, Calling iassign() to concat sort list4 | ||
var list5 = iassign(list4, function (l) { return l.sort(); }); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list5).toEqual([0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5]); | ||
expect(list5).not.toBe(list4); | ||
}); | ||
it("Example 2b: update list/array that pass undefined to getProp()", function () { | ||
//var 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; | ||
var list1 = [1, 2]; | ||
// 2.1: Calling iassign() to push items to list1 | ||
var list2 = iassign(list1, undefined, function (l) { l.push(3, 4, 5); return l; }); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list2).not.toBe(list1); | ||
// 2.2: Calling iassign() to unshift item to list2 | ||
var list3 = iassign(list2, undefined, function (l) { l.unshift(0); return l; }); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list3).not.toBe(list2); | ||
// 2.3, Calling iassign() to concat list1, list2 and list3 | ||
var list4 = iassign(list1, undefined, function (l) { return l.concat(list2, list3); }); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list4).not.toBe(list1); | ||
expect(list4).not.toBe(list2); | ||
expect(list4).not.toBe(list3); | ||
// 2.4, Calling iassign() to concat sort list4 | ||
var list5 = iassign(list4, undefined, function (l) { return l.sort(); }); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list5).toEqual([0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5]); | ||
expect(list5).not.toBe(list4); | ||
}); | ||
it("Example 3: update nested structures", function () { | ||
//var 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; | ||
var nested1 = { a: { b: { c: [3, 4, 5] } } }; | ||
// 3.1: Calling iassign() to assign d to nested1.a.b | ||
var nested2 = iassign(nested1, function (n) { return n.a.b; }, function (b) { b.d = 6; return b; }); | ||
expect(nested1).toEqual({ a: { b: { c: [3, 4, 5] } } }); | ||
expect(nested2).toEqual({ a: { b: { c: [3, 4, 5], d: 6 } } }); | ||
expect(nested2).not.toBe(nested1); | ||
// 3.2: Calling iassign() to increment nested2.a.b.d | ||
var nested3 = iassign(nested2, function (n) { return n.a.b.d; }, function (d) { return d + 1; }); | ||
expect(nested2).toEqual({ a: { b: { c: [3, 4, 5], d: 6 } } }); | ||
expect(nested3).toEqual({ a: { b: { c: [3, 4, 5], d: 7 } } }); | ||
expect(nested3).not.toBe(nested2); | ||
// 3.3: Calling iassign() to push item to nested3.a.b.c | ||
var nested4 = iassign(nested3, function (n) { return n.a.b.c; }, function (c) { c.push(6); return c; }); | ||
expect(nested3).toEqual({ a: { b: { c: [3, 4, 5], d: 7 } } }); | ||
expect(nested4).toEqual({ a: { b: { c: [3, 4, 5, 6], d: 7 } } }); | ||
expect(nested4).not.toBe(nested3); | ||
}); | ||
}); | ||
}); |
@@ -5,542 +5,815 @@ | ||
(function (root, factory) { | ||
if (typeof module === 'object' && typeof module.exports === 'object') { | ||
var v = factory(require, exports); if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === 'function' && define.amd) { | ||
define(["require", "exports"], factory); | ||
} else { | ||
// Browser globals (root is window) | ||
let browserRequire = (name) => { | ||
if (name == "deep-freeze" && root.deepFreeze) { | ||
return root.deepFreeze; | ||
} | ||
if (typeof module === 'object' && typeof module.exports === 'object') { | ||
var v = factory(require, exports); if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === 'function' && define.amd) { | ||
define(["require", "exports"], factory); | ||
} else { | ||
// Browser globals (root is window) | ||
let browserRequire = (name) => { | ||
if (name == "deep-freeze" && root.deepFreeze) { | ||
return root.deepFreeze; | ||
} | ||
if (name == "lodash" && root._) { | ||
return root._; | ||
} | ||
if (name == "lodash" && root._) { | ||
return root._; | ||
} | ||
if (name.indexOf("iassign") > -1 && root.iassign) { | ||
return root.iassign; | ||
} | ||
if (name.indexOf("iassign") > -1 && root.iassign) { | ||
return root.iassign; | ||
} | ||
throw new Error("Unable to require: " + name); | ||
throw new Error("Unable to require: " + name); | ||
} | ||
factory(browserRequire, {}); | ||
} | ||
factory(browserRequire, {}); | ||
} | ||
})(this, function (require, exports) { | ||
var iassign: IIassign = require("../src/iassign"); | ||
var deepFreeze: DeepFreeze.DeepFreezeInterface = require("deep-freeze"); | ||
var _: _.LoDashStatic = require("lodash"); | ||
var iassign: IIassign = require("../src/iassign"); | ||
var deepFreeze: DeepFreeze.DeepFreezeInterface = require("deep-freeze"); | ||
var _: _.LoDashStatic = require("lodash"); | ||
describe("Test", function () { | ||
describe("Test", function () { | ||
beforeEach(function () { | ||
}); | ||
beforeEach(function () { | ||
}); | ||
it("Access array item", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }; | ||
deepFreeze(o1); | ||
it("Access array item", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }; | ||
deepFreeze(o1); | ||
var o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[0][0], | ||
(ci) => { ci.d++; return ci; } | ||
); | ||
var o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[0][0], | ||
(ci) => { ci.d++; return ci; } | ||
); | ||
// | ||
// Jasmine Tests | ||
// | ||
// | ||
// Jasmine Tests | ||
// | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }) | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }) | ||
// expect o2 inner property has been updated. | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
// expect o2 inner property has been updated. | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2.a2).toBe(o1.a2); | ||
expect(o2.a.b2).toBe(o1.a.b2); | ||
expect(o2.a.b.c2).toBe(o1.a.b.c2); | ||
expect(o2.a.b.c[0][0].e).toBe(o1.a.b.c[0][0].e); | ||
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]); | ||
expect(o2.a.b.c[2][0]).toBe(o1.a.b.c[2][0]); | ||
}); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2.a2).toBe(o1.a2); | ||
expect(o2.a.b2).toBe(o1.a.b2); | ||
expect(o2.a.b.c2).toBe(o1.a.b.c2); | ||
expect(o2.a.b.c[0][0].e).toBe(o1.a.b.c[0][0].e); | ||
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]); | ||
expect(o2.a.b.c[2][0]).toBe(o1.a.b.c[2][0]); | ||
}); | ||
it("Access array 1", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }; | ||
deepFreeze(o1); | ||
it("Access array 1", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }; | ||
deepFreeze(o1); | ||
var o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[1], | ||
(c) => { c.push(<any>101); return c; } | ||
); | ||
var o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[1], | ||
(c) => { c.push(<any>101); return c; } | ||
); | ||
// | ||
// Jasmine Tests | ||
// | ||
// | ||
// Jasmine Tests | ||
// | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }) | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]], c2: {} }, b2: {} }, a2: {} }) | ||
// expect o2 inner property has been updated. | ||
expect(o2.a.b.c[1][1]).toBe(101); | ||
// expect o2 inner property has been updated. | ||
expect(o2.a.b.c[1][1]).toBe(101); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[1]).not.toBe(o1.a.b.c[1]); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[1]).not.toBe(o1.a.b.c[1]); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2.a2).toBe(o1.a2); | ||
expect(o2.a.b2).toBe(o1.a.b2); | ||
expect(o2.a.b.c2).toBe(o1.a.b.c2); | ||
expect(o2.a.b.c[0]).toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]); | ||
expect(o2.a.b.c[2]).toBe(o1.a.b.c[2]); | ||
expect(o2.a.b.c[2][0]).toBe(o1.a.b.c[2][0]); | ||
}); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2.a2).toBe(o1.a2); | ||
expect(o2.a.b2).toBe(o1.a.b2); | ||
expect(o2.a.b.c2).toBe(o1.a.b.c2); | ||
expect(o2.a.b.c[0]).toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]); | ||
expect(o2.a.b.c[2]).toBe(o1.a.b.c[2]); | ||
expect(o2.a.b.c[2][0]).toBe(o1.a.b.c[2][0]); | ||
}); | ||
it("Access array 2", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Access array 2", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
var o2 = iassign(o1, function (o) { return o.a.b.c; }, function (c) { c.pop(); return c; }); | ||
var o2 = iassign(o1, function (o) { return o.a.b.c; }, function (c) { c.pop(); return c; }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[1]).toBe(o1.a.b.c[1]); | ||
expect(o2.a.b.c[2]).toBe(undefined); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[1]).toBe(o1.a.b.c[1]); | ||
expect(o2.a.b.c[2]).toBe(undefined); | ||
}); | ||
it("Access object", function () { | ||
var o1 = { a: { b: { c: { d: 11, e: 12 } } } }; | ||
deepFreeze(o1); | ||
it("Access object", function () { | ||
var o1 = { a: { b: { c: { d: 11, e: 12 } } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a.b.c, (c) => { c.d++; return c }); | ||
let o2 = iassign(o1, (o) => o.a.b.c, (c) => { c.d++; return c }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c.d).not.toBe(o1.a.b.c.d); | ||
expect(o2.a.b.c.d).toBe(12); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c.d).not.toBe(o1.a.b.c.d); | ||
expect(o2.a.b.c.d).toBe(12); | ||
}); | ||
it("Access primitive", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Access primitive", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a.b.c[0][0].d, (d) => { return d + 1; }); | ||
let o2 = iassign(o1, (o) => o.a.b.c[0][0].d, (d) => { return d + 1; }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d); | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d); | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
}); | ||
it("Access date", function () { | ||
var o1 = { a: { b: { c: { d: 11, e: 12, f: new Date() } } } }; | ||
deepFreeze(o1); | ||
it("Access date", function () { | ||
var o1 = { a: { b: { c: { d: 11, e: 12, f: new Date() } } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a.b.c.f, (f) => { return new Date(2016, 1, 1) }); | ||
let o2 = iassign(o1, (o) => o.a.b.c.f, (f) => { return new Date(2016, 1, 1) }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c.f).not.toBe(o1.a.b.c.f); | ||
expect(o2.a.b.c.f).toEqual(new Date(2016, 1, 1)); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c.f).not.toBe(o1.a.b.c.f); | ||
expect(o2.a.b.c.f).toEqual(new Date(2016, 1, 1)); | ||
}); | ||
it("Access array item using string", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Access array item using string", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a.b.c["1"]["0"].d, (d) => { return d + 1; }); | ||
let o2 = iassign(o1, (o) => o.a.b.c["1"]["0"].d, (d) => { return d + 1; }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[1]).not.toBe(o1.a.b.c[1]); | ||
expect(o2.a.b.c[1][0]).not.toBe(o1.a.b.c[1][0]); | ||
expect(o2.a.b.c[1][0].d).not.toBe(o1.a.b.c[1][0].d); | ||
expect(o2.a.b.c[1][0].d).toBe(22); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[1]).not.toBe(o1.a.b.c[1]); | ||
expect(o2.a.b.c[1][0]).not.toBe(o1.a.b.c[1][0]); | ||
expect(o2.a.b.c[1][0].d).not.toBe(o1.a.b.c[1][0].d); | ||
expect(o2.a.b.c[1][0].d).toBe(22); | ||
}); | ||
it("Access object property using string", function () { | ||
var o1 = { a: { propB: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Access object property using string", function () { | ||
var o1 = { a: { propB: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a["propB"].c["1"][0].d, (d) => { return d + 1; }); | ||
let o2 = iassign(o1, (o) => o.a["propB"].c["1"][0].d, (d) => { return d + 1; }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.propB).not.toBe(o1.a.propB); | ||
expect(o2.a.propB.c).not.toBe(o1.a.propB.c); | ||
expect(o2.a.propB.c[1]).not.toBe(o1.a.propB.c[1]); | ||
expect(o2.a.propB.c[1][0]).not.toBe(o1.a.propB.c[1][0]); | ||
expect(o2.a.propB.c[1][0].d).not.toBe(o1.a.propB.c[1][0].d); | ||
expect(o2.a.propB.c[1][0].d).toBe(22); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.propB).not.toBe(o1.a.propB); | ||
expect(o2.a.propB.c).not.toBe(o1.a.propB.c); | ||
expect(o2.a.propB.c[1]).not.toBe(o1.a.propB.c[1]); | ||
expect(o2.a.propB.c[1][0]).not.toBe(o1.a.propB.c[1][0]); | ||
expect(o2.a.propB.c[1][0].d).not.toBe(o1.a.propB.c[1][0].d); | ||
expect(o2.a.propB.c[1][0].d).toBe(22); | ||
}); | ||
it("Access object property using string 2", function () { | ||
let propBName = "p\\r\"o.p t[] e.s\'t'B"; | ||
let propCName = "h\\e'llo w\'or\"ld"; | ||
var o1 = { a: { [propBName]: { [propCName]: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Access object property using string 2", function () { | ||
let propBName = "p\\r\"o.p t[] e.s\'t'B"; | ||
let propCName = "h\\e'llo w\'or\"ld"; | ||
var o1 = { a: { [propBName]: { [propCName]: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a["p\\r\"o.p t[] e.s\'t'B"]["h\\e'llo w\'or\"ld"]["1"][0].d, (d) => { return d + 1; }); | ||
let o2 = iassign(o1, (o) => o.a["p\\r\"o.p t[] e.s\'t'B"]["h\\e'llo w\'or\"ld"]["1"][0].d, (d) => { return d + 1; }); | ||
expect(o2.a[propBName][propCName][1][0].d).toBe(22); | ||
expect(o2.a[propBName][propCName][1][0].d).toBe(22); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a[propBName]).not.toBe(o1.a[propBName]); | ||
expect(o2.a[propBName][propCName]).not.toBe(o1.a[propBName][propCName]); | ||
expect(o2.a[propBName][propCName][1]).not.toBe(o1.a[propBName][propCName][1]); | ||
expect(o2.a[propBName][propCName][1][0]).not.toBe(o1.a[propBName][propCName][1][0]); | ||
expect(o2.a[propBName][propCName][1][0].d).not.toBe(o1.a[propBName][propCName][1][0].d); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a[propBName]).not.toBe(o1.a[propBName]); | ||
expect(o2.a[propBName][propCName]).not.toBe(o1.a[propBName][propCName]); | ||
expect(o2.a[propBName][propCName][1]).not.toBe(o1.a[propBName][propCName][1]); | ||
expect(o2.a[propBName][propCName][1][0]).not.toBe(o1.a[propBName][propCName][1][0]); | ||
expect(o2.a[propBName][propCName][1][0].d).not.toBe(o1.a[propBName][propCName][1][0].d); | ||
}); | ||
it("Access object property using string 3", function () { | ||
let propBName = "p\\ro.p t[] e.stB"; | ||
let propCName = "h\\e'llo w\'or\"ld"; | ||
let propDName = 'h\\e"llo w\"or\'ld'; | ||
let propEName = 'p\\ro.p t[] e.stB'; | ||
var o1 = { a: { [propBName]: { [propCName]: { [propDName]: { [propEName]: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } } } }; | ||
deepFreeze(o1); | ||
it("Access object property using string 3", function () { | ||
let propBName = "p\\ro.p t[] e.stB"; | ||
let propCName = "h\\e'llo w\'or\"ld"; | ||
let propDName = 'h\\e"llo w\"or\'ld'; | ||
let propEName = 'p\\ro.p t[] e.stB'; | ||
var o1 = { a: { [propBName]: { [propCName]: { [propDName]: { [propEName]: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a["p\\ro.p t[] e.stB"]["h\\e'llo w\'or\"ld"]['h\\e"llo w\"or\'ld']['p\\ro.p t[] e.stB']["1"][0].d, (d) => { return d + 1; }); | ||
let o2 = iassign(o1, (o) => o.a["p\\ro.p t[] e.stB"]["h\\e'llo w\'or\"ld"]['h\\e"llo w\"or\'ld']['p\\ro.p t[] e.stB']["1"][0].d, (d) => { return d + 1; }); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).toBe(22); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).toBe(22); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a[propBName]).not.toBe(o1.a[propBName]); | ||
expect(o2.a[propBName][propCName]).not.toBe(o1.a[propBName][propCName]); | ||
expect(o2.a[propBName][propCName][propDName]).not.toBe(o1.a[propBName][propCName][propDName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName]).not.toBe(o1.a[propBName][propCName][propDName][propEName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0].d); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a[propBName]).not.toBe(o1.a[propBName]); | ||
expect(o2.a[propBName][propCName]).not.toBe(o1.a[propBName][propCName]); | ||
expect(o2.a[propBName][propCName][propDName]).not.toBe(o1.a[propBName][propCName][propDName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName]).not.toBe(o1.a[propBName][propCName][propDName][propEName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0].d); | ||
}); | ||
it("Access object property using string 4", function () { | ||
let propBName = "p\\r\"o.p t[] e.s't\'B"; | ||
let propCName = "h\\e'llo w\'or\"ld"; | ||
let propDName = 'h\\e"llo w\"or\'ld'; | ||
let propEName = 'p\\r\'o.p t[] e.s"t\"B'; | ||
var o1 = { a: { [propBName]: { [propCName]: { [propDName]: { [propEName]: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } } } }; | ||
deepFreeze(o1); | ||
it("Access object property using string 4", function () { | ||
let propBName = "p\\r\"o.p t[] e.s't\'B"; | ||
let propCName = "h\\e'llo w\'or\"ld"; | ||
let propDName = 'h\\e"llo w\"or\'ld'; | ||
let propEName = 'p\\r\'o.p t[] e.s"t\"B'; | ||
var o1 = { a: { [propBName]: { [propCName]: { [propDName]: { [propEName]: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } } } }; | ||
deepFreeze(o1); | ||
let o2 = iassign(o1, (o) => o.a["p\\r\"o.p t[] e.s't\'B"]["h\\e'llo w\'or\"ld"]['h\\e"llo w\"or\'ld']['p\\r\'o.p t[] e.s"t\"B']["1"][0].d, (d) => { return d + 1; }); | ||
let o2 = iassign(o1, (o) => o.a["p\\r\"o.p t[] e.s't\'B"]["h\\e'llo w\'or\"ld"]['h\\e"llo w\"or\'ld']['p\\r\'o.p t[] e.s"t\"B']["1"][0].d, (d) => { return d + 1; }); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).toBe(22); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).toBe(22); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a[propBName]).not.toBe(o1.a[propBName]); | ||
expect(o2.a[propBName][propCName]).not.toBe(o1.a[propBName][propCName]); | ||
expect(o2.a[propBName][propCName][propDName]).not.toBe(o1.a[propBName][propCName][propDName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName]).not.toBe(o1.a[propBName][propCName][propDName][propEName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0].d); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a[propBName]).not.toBe(o1.a[propBName]); | ||
expect(o2.a[propBName][propCName]).not.toBe(o1.a[propBName][propCName]); | ||
expect(o2.a[propBName][propCName][propDName]).not.toBe(o1.a[propBName][propCName][propDName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName]).not.toBe(o1.a[propBName][propCName][propDName][propEName]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0]).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0]); | ||
expect(o2.a[propBName][propCName][propDName][propEName][1][0].d).not.toBe(o1.a[propBName][propCName][propDName][propEName][1][0].d); | ||
}); | ||
it("Access array using context parameter", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Access array using context parameter", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
var p1 = { a: 0 }; | ||
var o2 = iassign(o1, function (o, ctx) { return o.a.b.c[ctx.p1.a][0]; }, function (ci) { ci.d++; return ci; }, { p1 }); | ||
var p1 = { a: 0 }; | ||
var o2 = iassign(o1, function (o, ctx) { return o.a.b.c[ctx.p1.a][0]; }, function (ci) { ci.d++; return ci; }, { p1 }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d); | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
}); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d); | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
}); | ||
it("Try to modify freezed object should throw error.", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
it("Try to modify freezed object should throw error.", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
deepFreeze(o1); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].push(<any>3); return ci; }); | ||
}).toThrowError(TypeError, /Cannot|Can't|writable|doesn't|support|readonly/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].push(<any>3); return ci; }); | ||
}).toThrowError(TypeError, /Cannot|Can't|writable|doesn't|support|readonly/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { ci[0].d++; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|Cannot|read only|read-only|extensible|readonly/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { ci[0].d++; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|Cannot|read only|read-only|extensible|readonly/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { (<any>ci[0]).g = 1; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|add|extensible|readonly/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { (<any>ci[0]).g = 1; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|add|extensible|readonly/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].pop(); return ci; }); | ||
}).toThrowError(TypeError, /extensible|Cannot|can't|support|unable/i); | ||
}); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].pop(); return ci; }); | ||
}).toThrowError(TypeError, /extensible|Cannot|can't|support|unable/i); | ||
}); | ||
it("Update array using lodash", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }], [{ d: 21, e: 22 }]] } }, a2: {} }; | ||
it("Update array using lodash", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }], [{ d: 21, e: 22 }]] } }, a2: {} }; | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
// | ||
// Calling iassign() and _.map() to increment to d in c[0] array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[0], | ||
(c) => { | ||
return _.map(c, (item) => { return iassign(item, (o) => o.d, (d) => d + 1); }); | ||
// | ||
// Calling iassign() and _.map() to increment to d in c[0] array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[0], | ||
(c) => { | ||
return _.map(c, (item) => { return iassign(item, (o) => o.d, (d) => d + 1); }); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }], [{ d: 21, e: 22 }]] } }, a2: {} }) | ||
expect(o2).toEqual({ a: { b: { c: [[{ d: 12, e: 12 }, { d: 14, e: 14 }], [{ d: 21, e: 22 }]] } }, a2: {} }); | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][1]).not.toBe(o1.a.b.c[0][1]); | ||
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }], [{ d: 21, e: 22 }]] } }, a2: {} }) | ||
it("Update array using lodash 2", function () { | ||
var o1 = { a: { b: { c: [1, 2, 3] } } }; | ||
expect(o2).toEqual({ a: { b: { c: [[{ d: 12, e: 12 }, { d: 14, e: 14 }], [{ d: 21, e: 22 }]] } }, a2: {} }); | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][1]).not.toBe(o1.a.b.c[0][1]); | ||
// | ||
// Calling iassign() and _.map() to increment to every item in "c" array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c, | ||
(c) => { return _.map(c, (i) => i + 1); } | ||
); | ||
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [1, 2, 3] } } }) | ||
it("Update array using lodash 2", function () { | ||
var o1 = { a: { b: { c: [1, 2, 3] } } }; | ||
// expect o2.a.b.c has been updated. | ||
expect(o2.a.b.c).toEqual([2, 3, 4]); | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
}); | ||
// | ||
// Calling iassign() and _.map() to increment to every item in "c" array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c, | ||
(c) => { return _.map(c, (i) => i + 1); } | ||
); | ||
it("Test root object is an array", function () { | ||
var o1 = [[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]]; | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [1, 2, 3] } } }) | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
// expect o2.a.b.c has been updated. | ||
expect(o2.a.b.c).toEqual([2, 3, 4]); | ||
// | ||
// Calling iassign() and _.map() to increment to every item in "c" array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o[0], | ||
(o) => { | ||
return _.map(o, (item, index) => { | ||
if (index < 2) { | ||
item = _.cloneDeep(item); | ||
item.d++; | ||
} | ||
return item; | ||
}); | ||
} | ||
); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual([[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]]) | ||
it("Test root object is an array", function () { | ||
var o1 = [[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]]; | ||
// expect o2.a.b.c has been updated. | ||
expect(o2).toEqual([[{ d: 12, e: 12 }, { d: 14, e: 14 }, { d: 21, e: 22 }]]); | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2[0]).not.toBe(o1[0]); | ||
expect(o2[0][0]).not.toBe(o1[0][0]); | ||
expect(o2[0][1]).not.toBe(o1[0][1]); | ||
// | ||
// Calling iassign() and _.map() to increment to every item in "c" array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o[0], | ||
(o) => { | ||
return _.map(o, (item, index) => { | ||
if (index < 2) { | ||
item = _.cloneDeep(item); | ||
item.d++; | ||
} | ||
return item; | ||
}); | ||
} | ||
); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2[0][2]).toBe(o1[0][2]); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual([[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]]) | ||
it("Test root is an array, and try to update root array", function () { | ||
var o1 = [{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]; | ||
// expect o2.a.b.c has been updated. | ||
expect(o2).toEqual([[{ d: 12, e: 12 }, { d: 14, e: 14 }, { d: 21, e: 22 }]]); | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2[0]).not.toBe(o1[0]); | ||
expect(o2[0][0]).not.toBe(o1[0][0]); | ||
expect(o2[0][1]).not.toBe(o1[0][1]); | ||
// | ||
// Calling iassign() and _.map() to increment to every item in "c" array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o, | ||
(o) => { | ||
return _.map(o, (item, index) => { | ||
if (index < 2) { | ||
item = _.cloneDeep(item); | ||
item.d++; | ||
} | ||
return item; | ||
}); | ||
} | ||
); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2[0][2]).toBe(o1[0][2]); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual([{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]) | ||
it("Test root is an array, and try to update root array", function () { | ||
var o1 = [{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]; | ||
// expect o2.a.b.c has been updated. | ||
expect(o2).toEqual([{ d: 12, e: 12 }, { d: 14, e: 14 }, { d: 21, e: 22 }]); | ||
deepFreeze(o1); // Ensure o1 is not changed, for testing only | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2[0]).not.toBe(o1[0]); | ||
expect(o2[1]).not.toBe(o1[1]); | ||
// | ||
// Calling iassign() and _.map() to increment to every item in "c" array | ||
// | ||
let o2 = iassign( | ||
o1, | ||
(o) => o, | ||
(o) => { | ||
return _.map(o, (item, index) => { | ||
if (index < 2) { | ||
item = _.cloneDeep(item); | ||
item.d++; | ||
} | ||
return item; | ||
}); | ||
} | ||
); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2[2]).toBe(o1[2]); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual([{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]) | ||
it("Test root is an object, and try to update root object", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }; | ||
deepFreeze(o1); | ||
// expect o2.a.b.c has been updated. | ||
expect(o2).toEqual([{ d: 12, e: 12 }, { d: 14, e: 14 }, { d: 21, e: 22 }]); | ||
var o2 = iassign( | ||
o1, | ||
(o) => o, | ||
(o) => { o.a = <any>{ b: 1 }; return o; } | ||
); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2[0]).not.toBe(o1[0]); | ||
expect(o2[1]).not.toBe(o1[1]); | ||
// | ||
// Jasmine Tests | ||
// | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2[2]).toBe(o1[2]); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }); | ||
it("Test root is an object, and try to update root object", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }; | ||
deepFreeze(o1); | ||
// expect o2 inner property has been updated. | ||
expect(o2).toEqual({ a: { b: 1 }, a2: {} }); | ||
var o2 = iassign( | ||
o1, | ||
(o) => o, | ||
(o) => { o.a = <any>{ b: 1 }; return o; } | ||
); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
// | ||
// Jasmine Tests | ||
// | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2.a2).toBe(o1.a2); | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} }); | ||
it("Use built-in deep freeze to protect input", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
iassign.freezeInput = true; | ||
// expect o2 inner property has been updated. | ||
expect(o2).toEqual({ a: { b: 1 }, a2: {} }); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].push(<any>3); return ci; }); | ||
}).toThrowError(TypeError, /Cannot|Can't|writable|doesn't|support|readonly/i); | ||
// expect object graph for changed property in o2 is now different from (!==) o1. | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { ci[0].d++; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|Cannot|read only|read-only|extensible|readonly/i); | ||
// expect object graph for unchanged property in o2 is still equal to (===) o1. | ||
expect(o2.a2).toBe(o1.a2); | ||
}); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { (<any>ci[0]).g = 1; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|add|extensible|readonly/i); | ||
it("Use built-in deep freeze to protect input", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }], [{ d: 31, e: 32 }]] } } }; | ||
iassign.freezeInput = true; | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].pop(); return ci; }); | ||
}).toThrowError(TypeError, /extensible|Cannot|can't|support|unable/i); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].push(<any>3); return ci; }); | ||
}).toThrowError(TypeError, /Cannot|Can't|writable|doesn't|support|readonly/i); | ||
iassign.freezeInput = undefined; | ||
}); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { ci[0].d++; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|Cannot|read only|read-only|extensible|readonly/i); | ||
it("Use built-in deep freeze to protect output", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]] } } }; | ||
iassign.freezeOutput = true; | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c[0]; }, function (ci) { (<any>ci[0]).g = 1; return ci; }); | ||
}).toThrowError(TypeError, /Invalid|add|extensible|readonly/i); | ||
let o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[0], | ||
(c) => { | ||
return _.map(c, (item) => { return iassign(item, (o) => o.d, (d) => d + 1); }); | ||
}); | ||
expect(() => { | ||
iassign(o1, function (o) { return o.a.b.c; }, function (ci) { ci[0].pop(); return ci; }); | ||
}).toThrowError(TypeError, /extensible|Cannot|can't|support|unable/i); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]] } } }) | ||
iassign.freezeInput = undefined; | ||
}); | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
expect(o2.a.b.c[0][1].d).toBe(14); | ||
expect(o2.a.b.c[0][2].d).toBe(22); | ||
it("Use built-in deep freeze to protect output", function () { | ||
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]] } } }; | ||
iassign.freezeOutput = true; | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][1]).not.toBe(o1.a.b.c[0][1]); | ||
expect(o2.a.b.c[0][2]).not.toBe(o1.a.b.c[0][2]); | ||
let o2 = iassign( | ||
o1, | ||
(o) => o.a.b.c[0], | ||
(c) => { | ||
return _.map(c, (item) => { return iassign(item, (o) => o.d, (d) => d + 1); }); | ||
expect(() => { | ||
o2.a.b.c[0].push(<any>3); | ||
}).toThrowError(TypeError, /Cannot|Can't|writable|doesn't|support|readonly/i); | ||
expect(() => { | ||
o2.a.b.c[0][0].d++; | ||
}).toThrowError(TypeError, /Invalid|Cannot|read only|read-only|extensible|readonly/i); | ||
expect(() => { | ||
(<any>o2.a.b.c[0][0]).g = 1; | ||
}).toThrowError(TypeError, /Invalid|add|extensible|readonly/i); | ||
expect(() => { | ||
o2.a.b.c[0].pop(); | ||
}).toThrowError(TypeError, /extensible|Cannot|can't|support|unable/i); | ||
iassign.freezeOutput = undefined; | ||
}); | ||
// expect o1 has not been changed | ||
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }, { d: 13, e: 14 }, { d: 21, e: 22 }]] } } }) | ||
it("Example 1: update object", function () { | ||
//var iassign = require("immutable-assign"); | ||
expect(o2.a.b.c[0][0].d).toBe(12); | ||
expect(o2.a.b.c[0][1].d).toBe(14); | ||
expect(o2.a.b.c[0][2].d).toBe(22); | ||
// Deep freeze both input and output, can be used in development to make sure they don't change. | ||
iassign.freeze = true; | ||
expect(o2).not.toBe(o1); | ||
expect(o2.a).not.toBe(o1.a); | ||
expect(o2.a.b).not.toBe(o1.a.b); | ||
expect(o2.a.b.c).not.toBe(o1.a.b.c); | ||
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]); | ||
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]); | ||
expect(o2.a.b.c[0][1]).not.toBe(o1.a.b.c[0][1]); | ||
expect(o2.a.b.c[0][2]).not.toBe(o1.a.b.c[0][2]); | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
expect(() => { | ||
o2.a.b.c[0].push(<any>3); | ||
}).toThrowError(TypeError, /Cannot|Can't|writable|doesn't|support|readonly/i); | ||
expect(() => { | ||
o2.a.b.c[0][0].d++; | ||
}).toThrowError(TypeError, /Invalid|Cannot|read only|read-only|extensible|readonly/i); | ||
// 1: Calling iassign() to update map1.b | ||
var map2 = iassign( | ||
map1, | ||
(m) => { m.b = 50; return m; } | ||
); | ||
expect(() => { | ||
(<any>o2.a.b.c[0][0]).g = 1; | ||
}).toThrowError(TypeError, /Invalid|add|extensible|readonly/i); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
}); | ||
expect(() => { | ||
o2.a.b.c[0].pop(); | ||
}).toThrowError(TypeError, /extensible|Cannot|can't|support|unable/i); | ||
it("Example 1b: update object with option", function () { | ||
//var iassign = require("immutable-assign"); | ||
iassign.freezeOutput = undefined; | ||
// Deep freeze both input and output, can be used in development to make sure they don't change. | ||
iassign.freeze = true; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1: Calling iassign() to update map1.b | ||
var map2 = iassign( | ||
map1, | ||
(m) => { m.b = 50; return m; } | ||
); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
expect(() => { map2.a = 3; }).toThrow(); | ||
var map3 = iassign( | ||
map2, | ||
(m) => { m.c = 60; return m; }, | ||
{ freeze: false } | ||
); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map3).toEqual({ a: 1, b: 50, c: 60 }); | ||
expect(map3).not.toBe(map2); | ||
expect(() => { map3.a = 3; }).not.toThrow(); | ||
}); | ||
it("Example 1c: update object that pass undefined to getProp()", function () { | ||
//var 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; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1c: Calling iassign() to update map1.b | ||
var map2 = iassign<any, { b: number }, any>( | ||
map1, | ||
undefined, | ||
(m) => { m.b = 50; return m; } | ||
); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
}); | ||
it("Example 1d: update object with option that pass undefined to getProp()", function () { | ||
//var 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; | ||
var map1 = { a: 1, b: 2, c: 3 }; | ||
// 1: Calling iassign() to update map1.b | ||
var map2 = iassign<any, {b: number}, any>( | ||
map1, | ||
undefined, | ||
(m) => { m.b = 50; return m; } | ||
); | ||
expect(map1).toEqual({ a: 1, b: 2, c: 3 }); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map2).not.toBe(map1); | ||
expect(() => { map2.a = 3; }).toThrow(); | ||
var map3 = iassign<any, {c: number}, any>( | ||
map2, | ||
undefined, | ||
(m) => { m.c = 60; return m; }, | ||
undefined, | ||
{ freeze: false } | ||
); | ||
expect(map2).toEqual({ a: 1, b: 50, c: 3 }); | ||
expect(map3).toEqual({ a: 1, b: 50, c: 60 }); | ||
expect(map3).not.toBe(map2); | ||
expect(() => { map3.a = 3; }).not.toThrow(); | ||
}); | ||
it("Example 2: update list/array", function () { | ||
//var 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; | ||
var list1 = [1, 2]; | ||
// 2.1: Calling iassign() to push items to list1 | ||
var list2 = iassign( | ||
list1, | ||
function (l) { l.push(3, 4, 5); return l; } | ||
); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list2).not.toBe(list1); | ||
// 2.2: Calling iassign() to unshift item to list2 | ||
var list3 = iassign( | ||
list2, | ||
function (l) { l.unshift(0); return l; } | ||
); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list3).not.toBe(list2); | ||
// 2.3, Calling iassign() to concat list1, list2 and list3 | ||
var list4 = iassign( | ||
list1, | ||
function (l) { return l.concat(list2, list3); } | ||
); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list4).not.toBe(list1); | ||
expect(list4).not.toBe(list2); | ||
expect(list4).not.toBe(list3); | ||
// 2.4, Calling iassign() to concat sort list4 | ||
var list5 = iassign( | ||
list4, | ||
function (l) { return l.sort(); } | ||
); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list5).toEqual([0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5]); | ||
expect(list5).not.toBe(list4); | ||
}); | ||
it("Example 2b: update list/array that pass undefined to getProp()", function () { | ||
//var 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; | ||
var list1 = [1, 2]; | ||
// 2.1: Calling iassign() to push items to list1 | ||
var list2 = iassign<any, number[], any>( | ||
list1, | ||
undefined, | ||
function (l) { l.push(3, 4, 5); return l; } | ||
); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list2).not.toBe(list1); | ||
// 2.2: Calling iassign() to unshift item to list2 | ||
var list3 = iassign<any, number[], any>( | ||
list2, | ||
undefined, | ||
function (l) { l.unshift(0); return l; } | ||
); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list3).not.toBe(list2); | ||
// 2.3, Calling iassign() to concat list1, list2 and list3 | ||
var list4 = iassign<any, number[], any>( | ||
list1, | ||
undefined, | ||
function (l) { return l.concat(list2, list3); } | ||
); | ||
expect(list1).toEqual([1, 2]); | ||
expect(list2).toEqual([1, 2, 3, 4, 5]); | ||
expect(list3).toEqual([0, 1, 2, 3, 4, 5]); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list4).not.toBe(list1); | ||
expect(list4).not.toBe(list2); | ||
expect(list4).not.toBe(list3); | ||
// 2.4, Calling iassign() to concat sort list4 | ||
var list5 = iassign<any, number[], any>( | ||
list4, | ||
undefined, | ||
function (l) { return l.sort(); } | ||
); | ||
expect(list4).toEqual([1, 2, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); | ||
expect(list5).toEqual([0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5]); | ||
expect(list5).not.toBe(list4); | ||
}); | ||
it("Example 3: update nested structures", function () { | ||
//var 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; | ||
var nested1 = { a: { b: { c: [3, 4, 5] } } }; | ||
// 3.1: Calling iassign() to assign d to nested1.a.b | ||
var nested2 = iassign( | ||
nested1, | ||
function (n) { return n.a.b; }, | ||
function (b: any) { b.d = 6; return b; } | ||
); | ||
expect(nested1).toEqual({ a: { b: { c: [3, 4, 5] } } }); | ||
expect(nested2).toEqual({ a: { b: { c: [3, 4, 5], d: 6 } } }); | ||
expect(nested2).not.toBe(nested1); | ||
// 3.2: Calling iassign() to increment nested2.a.b.d | ||
var nested3 = iassign( | ||
nested2, | ||
function (n) { return (<any>n.a.b).d; }, | ||
function (d) { return d + 1; } | ||
); | ||
expect(nested2).toEqual({ a: { b: { c: [3, 4, 5], d: 6 } } }); | ||
expect(nested3).toEqual({ a: { b: { c: [3, 4, 5], d: 7 } } }); | ||
expect(nested3).not.toBe(nested2); | ||
// 3.3: Calling iassign() to push item to nested3.a.b.c | ||
var nested4 = iassign( | ||
nested3, | ||
function (n) { return n.a.b.c; }, | ||
function (c) { c.push(6); return c; } | ||
); | ||
expect(nested3).toEqual({ a: { b: { c: [3, 4, 5], d: 7 } } }); | ||
expect(nested4).toEqual({ a: { b: { c: [3, 4, 5, 6], d: 7 } } }); | ||
expect(nested4).not.toBe(nested3); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -5,20 +5,38 @@ | ||
interface IIassignOption { | ||
freeze: boolean; // Deep freeze both input and output | ||
freezeInput: boolean; // Deep freeze input | ||
freezeOutput: boolean; // Deep freeze output | ||
disableAllCheck: boolean; | ||
disableHasReturnCheck: boolean; | ||
freeze?: boolean; // Deep freeze both input and output | ||
freezeInput?: boolean; // Deep freeze input | ||
freezeOutput?: boolean; // Deep freeze output | ||
disableAllCheck?: boolean; | ||
disableHasReturnCheck?: boolean; | ||
// Disable validation for extra statements in the getProp() function, | ||
// which is needed when running the coverage, e.g., istanbul.js does add | ||
// instrument statements in our getProp() function, which can be safely ignored. | ||
disableExtraStatementCheck: boolean; | ||
disableExtraStatementCheck?: boolean; | ||
} | ||
type getPropFunc<TObj, TProp, TContext> = (obj: TObj, context: TContext) => TProp; | ||
type setPropFunc<TProp> = (prop: TProp) => TProp; | ||
interface IIassign extends IIassignOption { | ||
// Intellisense for the TObj parameter in getProp will only work if we remove the auto added closing bracket of iassign, | ||
// and manually add the closing bracket at last. i.e., | ||
// | ||
// 1. Type iassign( in the editor | ||
// 2. Most editor will auto complete with closing bracket, e.g., iassign() | ||
// 3. If we continue to type without removing the closing bracket, e.g., iassign(nested, (n) => n.), | ||
// editor such as VS Code will not show any intellisense for "n" | ||
// 4. We must remove the closing bracket of iassign(), and intellisense will be shown for "n" | ||
<TObj, TProp, TContext>( | ||
obj: TObj, | ||
getProp: (obj: TObj, context: TContext) => TProp, | ||
setProp: (prop: TProp) => TProp, | ||
getProp: getPropFunc<TObj, TProp, TContext>, | ||
setProp: setPropFunc<TProp>, | ||
context?: TContext, | ||
option?: IIassignOption): TObj; | ||
<TObj>( | ||
obj: TObj, | ||
setProp: setPropFunc<TObj>, | ||
option?: IIassignOption): TObj; | ||
} | ||
@@ -25,0 +43,0 @@ } |
@@ -32,6 +32,16 @@ "use strict"; | ||
function _iassign(obj, // Object to set property, it will not be modified. | ||
getProp, // Function to get property to be updated. Must be pure function. | ||
setProp, // Function to set property. | ||
context, // (Optional) Context to be used in getProp(). | ||
option) { | ||
getPropOrSetProp, // 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 setProp = setPropOrOption; | ||
var context = contextOrUndefined; | ||
var option = optionOrUndefined; | ||
if (typeof setPropOrOption !== "function") { | ||
getProp = undefined; | ||
setProp = getPropOrSetProp; | ||
context = undefined; | ||
option = setPropOrOption; | ||
} | ||
if (option) { | ||
@@ -46,89 +56,95 @@ option = extend({}, iassign, option); | ||
} | ||
// Check if getProp() is valid | ||
var value = getProp(obj, context); | ||
var getPropFuncInfo = parseGetPropFuncInfo(getProp, option); | ||
var accessorText = getPropFuncInfo.accessorText; | ||
var propIndex = 0; | ||
var propValue = undefined; | ||
while (accessorText) { | ||
var openBracketIndex = accessorText.indexOf("["); | ||
var closeBracketIndex = accessorText.indexOf("]"); | ||
var dotIndex = accessorText.indexOf("."); | ||
var propName = ""; | ||
var propNameSource = ePropNameSource.none; | ||
// if (dotIndex == 0) { | ||
// accessorText = accessorText.substr(dotIndex + 1); | ||
// continue; | ||
// } | ||
if (openBracketIndex > -1 && closeBracketIndex <= -1) { | ||
throw new Error("Found open bracket but not close bracket."); | ||
} | ||
if (openBracketIndex <= -1 && closeBracketIndex > -1) { | ||
throw new Error("Found close bracket but not open bracket."); | ||
} | ||
if (dotIndex > -1 && (dotIndex < openBracketIndex || openBracketIndex <= -1)) { | ||
propName = accessorText.substr(0, dotIndex); | ||
accessorText = accessorText.substr(dotIndex + 1); | ||
propNameSource = ePropNameSource.beforeDot; | ||
} | ||
else if (openBracketIndex > -1 && (openBracketIndex < dotIndex || dotIndex <= -1)) { | ||
if (openBracketIndex > 0) { | ||
propName = accessorText.substr(0, openBracketIndex); | ||
accessorText = accessorText.substr(openBracketIndex); | ||
propNameSource = ePropNameSource.beforeBracket; | ||
if (!getProp) { | ||
obj = quickCopy(obj); | ||
obj = setProp(obj); | ||
} | ||
else { | ||
// Check if getProp() is valid | ||
var value = getProp(obj, context); | ||
var getPropFuncInfo = parseGetPropFuncInfo(getProp, option); | ||
var accessorText = getPropFuncInfo.accessorText; | ||
var propIndex = 0; | ||
var propValue = undefined; | ||
while (accessorText) { | ||
var openBracketIndex = accessorText.indexOf("["); | ||
var closeBracketIndex = accessorText.indexOf("]"); | ||
var dotIndex = accessorText.indexOf("."); | ||
var propName = ""; | ||
var propNameSource = ePropNameSource.none; | ||
// if (dotIndex == 0) { | ||
// accessorText = accessorText.substr(dotIndex + 1); | ||
// continue; | ||
// } | ||
if (openBracketIndex > -1 && closeBracketIndex <= -1) { | ||
throw new Error("Found open bracket but not close bracket."); | ||
} | ||
if (openBracketIndex <= -1 && closeBracketIndex > -1) { | ||
throw new Error("Found close bracket but not open bracket."); | ||
} | ||
if (dotIndex > -1 && (dotIndex < openBracketIndex || openBracketIndex <= -1)) { | ||
propName = accessorText.substr(0, dotIndex); | ||
accessorText = accessorText.substr(dotIndex + 1); | ||
propNameSource = ePropNameSource.beforeDot; | ||
} | ||
else if (openBracketIndex > -1 && (openBracketIndex < dotIndex || dotIndex <= -1)) { | ||
if (openBracketIndex > 0) { | ||
propName = accessorText.substr(0, openBracketIndex); | ||
accessorText = accessorText.substr(openBracketIndex); | ||
propNameSource = ePropNameSource.beforeBracket; | ||
} | ||
else { | ||
propName = accessorText.substr(openBracketIndex + 1, closeBracketIndex - 1); | ||
accessorText = accessorText.substr(closeBracketIndex + 1); | ||
propNameSource = ePropNameSource.inBracket; | ||
} | ||
} | ||
else { | ||
propName = accessorText.substr(openBracketIndex + 1, closeBracketIndex - 1); | ||
accessorText = accessorText.substr(closeBracketIndex + 1); | ||
propNameSource = ePropNameSource.inBracket; | ||
propName = accessorText; | ||
accessorText = ""; | ||
propNameSource = ePropNameSource.last; | ||
} | ||
} | ||
else { | ||
propName = accessorText; | ||
accessorText = ""; | ||
propNameSource = ePropNameSource.last; | ||
} | ||
propName = propName.trim(); | ||
if (propName == "") { | ||
continue; | ||
} | ||
//console.log(propName); | ||
if (propIndex <= 0) { | ||
propValue = quickCopy(obj); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
propName = propName.trim(); | ||
if (propName == "") { | ||
continue; | ||
} | ||
obj = propValue; | ||
} | ||
else { | ||
var prevPropValue = propValue; | ||
if (propNameSource == ePropNameSource.inBracket && isNaN(propName)) { | ||
if (propName[0] == "#") { | ||
var quotedPropName = getPropFuncInfo.quotedTextInfos[propName]; | ||
if (!quotedPropName) { | ||
throw new Error("Cannot find quoted text for " + quotedPropName); | ||
} | ||
propName = eval(quotedPropName); | ||
//console.log(propName); | ||
if (propIndex <= 0) { | ||
propValue = quickCopy(obj); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
else { | ||
var statement = "'use strict';\n"; | ||
if (getPropFuncInfo.objParameterName) { | ||
statement += "var " + getPropFuncInfo.objParameterName + " = arguments[1];\n"; | ||
obj = propValue; | ||
} | ||
else { | ||
var prevPropValue = propValue; | ||
if (propNameSource == ePropNameSource.inBracket && isNaN(propName)) { | ||
if (propName[0] == "#") { | ||
var quotedPropName = getPropFuncInfo.quotedTextInfos[propName]; | ||
if (!quotedPropName) { | ||
throw new Error("Cannot find quoted text for " + quotedPropName); | ||
} | ||
propName = eval(quotedPropName); | ||
} | ||
if (getPropFuncInfo.cxtParameterName) { | ||
statement += "var " + getPropFuncInfo.cxtParameterName + " = arguments[2];\n"; | ||
else { | ||
var statement = "'use strict';\n"; | ||
if (getPropFuncInfo.objParameterName) { | ||
statement += "var " + getPropFuncInfo.objParameterName + " = arguments[1];\n"; | ||
} | ||
if (getPropFuncInfo.cxtParameterName) { | ||
statement += "var " + getPropFuncInfo.cxtParameterName + " = arguments[2];\n"; | ||
} | ||
statement += "" + propName; | ||
propName = evalStatement(statement, obj, context); | ||
} | ||
statement += "" + propName; | ||
propName = evalStatement(statement, obj, context); | ||
} | ||
propValue = propValue[propName]; | ||
propValue = quickCopy(propValue); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
prevPropValue[propName] = propValue; | ||
} | ||
propValue = propValue[propName]; | ||
propValue = quickCopy(propValue); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
prevPropValue[propName] = propValue; | ||
//console.log(propValue); | ||
propIndex++; | ||
} | ||
//console.log(propValue); | ||
propIndex++; | ||
} | ||
@@ -135,0 +151,0 @@ if (deepFreeze && (option.freeze || option.freezeOutput)) { |
@@ -8,20 +8,38 @@ "use strict"; | ||
interface IIassignOption { | ||
freeze: boolean; // Deep freeze both input and output | ||
freezeInput: boolean; // Deep freeze input | ||
freezeOutput: boolean; // Deep freeze output | ||
disableAllCheck: boolean; | ||
disableHasReturnCheck: boolean; | ||
freeze?: boolean; // Deep freeze both input and output | ||
freezeInput?: boolean; // Deep freeze input | ||
freezeOutput?: boolean; // Deep freeze output | ||
disableAllCheck?: boolean; | ||
disableHasReturnCheck?: boolean; | ||
// Disable validation for extra statements in the getProp() function, | ||
// which is needed when running the coverage, e.g., istanbul.js does add | ||
// instrument statements in our getProp() function, which can be safely ignored. | ||
disableExtraStatementCheck: boolean; | ||
disableExtraStatementCheck?: boolean; | ||
} | ||
type getPropFunc<TObj, TProp, TContext> = (obj: TObj, context: TContext) => TProp; | ||
type setPropFunc<TProp> = (prop: TProp) => TProp; | ||
interface IIassign extends IIassignOption { | ||
// Intellisense for the TObj parameter in getProp will only work if we remove the auto added closing bracket of iassign, | ||
// and manually add the closing bracket at last. i.e., | ||
// | ||
// 1. Type iassign( in the editor | ||
// 2. Most editor will auto complete with closing bracket, e.g., iassign() | ||
// 3. If we continue to type without removing the closing bracket, e.g., iassign(nested, (n) => n.), | ||
// editor such as VS Code will not show any intellisense for "n" | ||
// 4. We must remove the closing bracket of iassign(), and intellisense will be shown for "n" | ||
<TObj, TProp, TContext>( | ||
obj: TObj, | ||
getProp: (obj: TObj, context: TContext) => TProp, | ||
setProp: (prop: TProp) => TProp, | ||
getProp: getPropFunc<TObj, TProp, TContext>, | ||
setProp: setPropFunc<TProp>, | ||
context?: TContext, | ||
option?: IIassignOption): TObj; | ||
<TObj>( | ||
obj: TObj, | ||
setProp: setPropFunc<TObj>, | ||
option?: IIassignOption): TObj; | ||
} | ||
@@ -60,8 +78,20 @@ | ||
function _iassign<TObj, TProp, TContext>( | ||
obj: TObj, // Object to set property, it will not be modified. | ||
getProp: (obj: TObj, context: TContext) => TProp, // Function to get property to be updated. Must be pure function. | ||
setProp: (prop: TProp) => TProp, // Function to set property. | ||
context?: TContext, // (Optional) Context to be used in getProp(). | ||
option?: IIassignOption): TObj { | ||
obj: TObj, // Object to set property, it will not be modified. | ||
getPropOrSetProp: getPropFunc<TObj, TProp, TContext> | setPropFunc<TProp>, // Function to get property to be updated. Must be pure function. | ||
setPropOrOption: setPropFunc<TProp> | IIassignOption, // Function to set property. | ||
contextOrUndefined?: TContext, // (Optional) Context to be used in getProp(). | ||
optionOrUndefined?: IIassignOption): TObj { | ||
let getProp = <getPropFunc<TObj, TProp, TContext>>getPropOrSetProp; | ||
let setProp = <setPropFunc<TProp>>setPropOrOption; | ||
let context = contextOrUndefined; | ||
let option = optionOrUndefined; | ||
if (typeof setPropOrOption !== "function") { | ||
getProp = undefined; | ||
setProp = <setPropFunc<TProp>>getPropOrSetProp; | ||
context = undefined; | ||
option = <IIassignOption>setPropOrOption; | ||
} | ||
if (option) { | ||
@@ -78,109 +108,115 @@ option = extend({}, iassign, option); | ||
// Check if getProp() is valid | ||
let value = getProp(obj, context); | ||
if (!getProp) { | ||
obj = quickCopy(obj); | ||
obj = <any>setProp(<any>obj); | ||
} | ||
else { | ||
// Check if getProp() is valid | ||
let value = getProp(obj, context); | ||
let getPropFuncInfo = parseGetPropFuncInfo(getProp, option); | ||
let accessorText = getPropFuncInfo.accessorText; | ||
let getPropFuncInfo = parseGetPropFuncInfo(getProp, option); | ||
let accessorText = getPropFuncInfo.accessorText; | ||
let propIndex = 0; | ||
let propValue = undefined; | ||
let propIndex = 0; | ||
let propValue = undefined; | ||
while (accessorText) { | ||
let openBracketIndex = accessorText.indexOf("["); | ||
let closeBracketIndex = accessorText.indexOf("]"); | ||
let dotIndex = accessorText.indexOf("."); | ||
let propName = ""; | ||
let propNameSource = ePropNameSource.none; | ||
while (accessorText) { | ||
let openBracketIndex = accessorText.indexOf("["); | ||
let closeBracketIndex = accessorText.indexOf("]"); | ||
let dotIndex = accessorText.indexOf("."); | ||
let propName = ""; | ||
let propNameSource = ePropNameSource.none; | ||
// if (dotIndex == 0) { | ||
// accessorText = accessorText.substr(dotIndex + 1); | ||
// continue; | ||
// } | ||
// if (dotIndex == 0) { | ||
// accessorText = accessorText.substr(dotIndex + 1); | ||
// continue; | ||
// } | ||
if (openBracketIndex > -1 && closeBracketIndex <= -1) { | ||
throw new Error("Found open bracket but not close bracket."); | ||
} | ||
if (openBracketIndex > -1 && closeBracketIndex <= -1) { | ||
throw new Error("Found open bracket but not close bracket."); | ||
} | ||
if (openBracketIndex <= -1 && closeBracketIndex > -1) { | ||
throw new Error("Found close bracket but not open bracket."); | ||
} | ||
if (openBracketIndex <= -1 && closeBracketIndex > -1) { | ||
throw new Error("Found close bracket but not open bracket."); | ||
} | ||
if (dotIndex > -1 && (dotIndex < openBracketIndex || openBracketIndex <= -1)) { | ||
propName = accessorText.substr(0, dotIndex); | ||
accessorText = accessorText.substr(dotIndex + 1); | ||
propNameSource = ePropNameSource.beforeDot; | ||
} | ||
else if (openBracketIndex > -1 && (openBracketIndex < dotIndex || dotIndex <= -1)) { | ||
if (dotIndex > -1 && (dotIndex < openBracketIndex || openBracketIndex <= -1)) { | ||
propName = accessorText.substr(0, dotIndex); | ||
accessorText = accessorText.substr(dotIndex + 1); | ||
propNameSource = ePropNameSource.beforeDot; | ||
} | ||
else if (openBracketIndex > -1 && (openBracketIndex < dotIndex || dotIndex <= -1)) { | ||
if (openBracketIndex > 0) { | ||
propName = accessorText.substr(0, openBracketIndex); | ||
accessorText = accessorText.substr(openBracketIndex); | ||
propNameSource = ePropNameSource.beforeBracket; | ||
if (openBracketIndex > 0) { | ||
propName = accessorText.substr(0, openBracketIndex); | ||
accessorText = accessorText.substr(openBracketIndex); | ||
propNameSource = ePropNameSource.beforeBracket; | ||
} | ||
else { | ||
propName = accessorText.substr(openBracketIndex + 1, closeBracketIndex - 1); | ||
accessorText = accessorText.substr(closeBracketIndex + 1); | ||
propNameSource = ePropNameSource.inBracket; | ||
} | ||
} | ||
else { | ||
propName = accessorText.substr(openBracketIndex + 1, closeBracketIndex - 1); | ||
accessorText = accessorText.substr(closeBracketIndex + 1); | ||
propNameSource = ePropNameSource.inBracket; | ||
propName = accessorText; | ||
accessorText = ""; | ||
propNameSource = ePropNameSource.last; | ||
} | ||
} | ||
else { | ||
propName = accessorText; | ||
accessorText = ""; | ||
propNameSource = ePropNameSource.last; | ||
} | ||
propName = propName.trim(); | ||
if (propName == "") { | ||
continue; | ||
} | ||
propName = propName.trim(); | ||
if (propName == "") { | ||
continue; | ||
} | ||
//console.log(propName); | ||
//console.log(propName); | ||
if (propIndex <= 0) { | ||
propValue = quickCopy(obj); | ||
if (propIndex <= 0) { | ||
propValue = quickCopy(obj); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
obj = propValue; | ||
} | ||
else { | ||
let prevPropValue = propValue; | ||
obj = propValue; | ||
} | ||
else { | ||
let prevPropValue = propValue; | ||
if (propNameSource == ePropNameSource.inBracket && isNaN(<any>propName)) { | ||
if (propNameSource == ePropNameSource.inBracket && isNaN(<any>propName)) { | ||
if (propName[0] == "#") { | ||
let quotedPropName = getPropFuncInfo.quotedTextInfos[propName]; | ||
if (!quotedPropName) { | ||
throw new Error("Cannot find quoted text for " + quotedPropName); | ||
if (propName[0] == "#") { | ||
let quotedPropName = getPropFuncInfo.quotedTextInfos[propName]; | ||
if (!quotedPropName) { | ||
throw new Error("Cannot find quoted text for " + quotedPropName); | ||
} | ||
propName = eval(quotedPropName); | ||
} | ||
propName = eval(quotedPropName); | ||
} | ||
else { | ||
let statement = `'use strict';\n`; | ||
if (getPropFuncInfo.objParameterName) { | ||
statement += `var ${getPropFuncInfo.objParameterName} = arguments[1];\n` | ||
else { | ||
let statement = `'use strict';\n`; | ||
if (getPropFuncInfo.objParameterName) { | ||
statement += `var ${getPropFuncInfo.objParameterName} = arguments[1];\n` | ||
} | ||
if (getPropFuncInfo.cxtParameterName) { | ||
statement += `var ${getPropFuncInfo.cxtParameterName} = arguments[2];\n` | ||
} | ||
statement += `${propName}`; | ||
propName = (<any>evalStatement)(statement, obj, context); | ||
} | ||
if (getPropFuncInfo.cxtParameterName) { | ||
statement += `var ${getPropFuncInfo.cxtParameterName} = arguments[2];\n` | ||
} | ||
statement += `${propName}`; | ||
propName = (<any>evalStatement)(statement, obj, context); | ||
} | ||
} | ||
propValue = propValue[propName]; | ||
propValue = quickCopy(propValue) | ||
propValue = propValue[propName]; | ||
propValue = quickCopy(propValue) | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
if (!accessorText) { | ||
propValue = setProp(propValue); | ||
} | ||
prevPropValue[propName] = propValue; | ||
} | ||
prevPropValue[propName] = propValue; | ||
//console.log(propValue); | ||
propIndex++; | ||
} | ||
//console.log(propValue); | ||
propIndex++; | ||
} | ||
@@ -187,0 +223,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
858081
24299
368