diff --git a/cjs/object-path-immutable.js b/cjs/object-path-immutable.js index ea0a262..9cf3c50 100644 --- a/cjs/object-path-immutable.js +++ b/cjs/object-path-immutable.js @@ -55,37 +55,6 @@ function getKey (key) { return key } -var objectPathImmutable = function (src) { - var dest = src; - var committed = false; - - var transaction = Object.keys(api).reduce(function (proxy, prop) { - /* istanbul ignore else */ - if (typeof api[prop] === 'function') { - proxy[prop] = function () { - var args = [dest, src].concat(Array.prototype.slice.call(arguments)); - - if (committed) { - throw new Error('Cannot call ' + prop + ' after `value`') - } - - dest = api[prop].apply(null, args); - - return transaction - }; - } - - return proxy - }, {}); - - transaction.value = function () { - committed = true; - return dest - }; - - return transaction -}; - function clone (obj, createIfEmpty, assumeArray) { if (obj == null) { if (createIfEmpty) { @@ -104,13 +73,13 @@ function clone (obj, createIfEmpty, assumeArray) { return assignToObj({}, obj) } -function deepMerge (dest, src) { +function _deepMerge (dest, src) { if (dest !== src && isPlainObject(dest) && isPlainObject(src)) { var merged = {}; for (var key in dest) { if (_hasOwnProperty.call(dest, key)) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]); + merged[key] = _deepMerge(dest[key], src[key]); } else { merged[key] = dest[key]; } @@ -119,7 +88,7 @@ function deepMerge (dest, src) { for (key in src) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]); + merged[key] = _deepMerge(dest[key], src[key]); } } return merged @@ -127,7 +96,7 @@ function deepMerge (dest, src) { return src } -function changeImmutable (dest, src, path, changeCallback) { +function _changeImmutable (dest, src, path, changeCallback) { if (isNumber(path)) { path = [path]; } @@ -135,7 +104,7 @@ function changeImmutable (dest, src, path, changeCallback) { return src } if (isString(path)) { - return changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) + return _changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) } var currentPath = path[0]; @@ -151,7 +120,7 @@ function changeImmutable (dest, src, path, changeCallback) { src = src[currentPath]; } - dest[currentPath] = changeImmutable(dest[currentPath], src, path.slice(1), changeCallback); + dest[currentPath] = _changeImmutable(dest[currentPath], src, path.slice(1), changeCallback); return dest } @@ -161,7 +130,7 @@ api.set = function set (dest, src, path, value) { if (isEmpty(path)) { return value } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = value; return clonedObj }) @@ -171,7 +140,7 @@ api.update = function update (dest, src, path, updater) { if (isEmpty(path)) { return updater(clone(src)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = updater(clonedObj[finalPath]); return clonedObj }) @@ -186,7 +155,7 @@ api.push = function push (dest, src, path /*, values */) { return src.concat(values) } } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (!isArray(clonedObj[finalPath])) { clonedObj[finalPath] = values; } else { @@ -207,7 +176,7 @@ api.insert = function insert (dest, src, path, value, at) { first.push(value); return first.concat(src.slice(at)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { var arr = clonedObj[finalPath]; if (!isArray(arr)) { if (arr != null && typeof arr !== 'undefined') { @@ -227,7 +196,7 @@ api.del = function del (dest, src, path) { if (isEmpty(path)) { return undefined } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (Array.isArray(clonedObj)) { if (clonedObj[finalPath] !== undefined) { clonedObj.splice(finalPath, 1); @@ -248,7 +217,7 @@ api.assign = function assign (dest, src, path, source) { } return assignToObj(clone(src), source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source); var target = clone(clonedObj[finalPath], true); assignToObj(target, source); @@ -263,17 +232,59 @@ api.merge = function assign (dest, src, path, source) { if (isEmpty(source)) { return src } - return deepMerge(src, source) + return _deepMerge(src, source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source); - clonedObj[finalPath] = deepMerge(clonedObj[finalPath], source); + clonedObj[finalPath] = _deepMerge(clonedObj[finalPath], source); return clonedObj }) }; -module.exports = Object.keys(api).reduce(function (objectPathImmutable, method) { - objectPathImmutable[method] = api[method].bind(null, null); +function wrap (src) { + var dest = src; + var committed = false; + + var transaction = Object.keys(api).reduce(function (proxy, prop) { + /* istanbul ignore else */ + if (typeof api[prop] === 'function') { + proxy[prop] = function () { + var args = [dest, src].concat(Array.prototype.slice.call(arguments)); + + if (committed) { + throw new Error('Cannot call ' + prop + ' after `value`') + } + + dest = api[prop].apply(null, args); + + return transaction + }; + } + + return proxy + }, {}); + + transaction.value = function () { + committed = true; + return dest + }; + + return transaction +} - return objectPathImmutable -}, objectPathImmutable); +var set = api.set.bind(null, null); +var update = api.update.bind(null, null); +var push = api.push.bind(null, null); +var insert = api.insert.bind(null, null); +var del = api.del.bind(null, null); +var assign = api.assign.bind(null, null); +var merge = api.merge.bind(null, null); + +exports.assign = assign; +exports.del = del; +exports.insert = insert; +exports.merge = merge; +exports.push = push; +exports.set = set; +exports.update = update; +exports.wrap = wrap; diff --git a/esm/object-path-immutable.js b/esm/object-path-immutable.js index def7160..5b0b476 100644 --- a/esm/object-path-immutable.js +++ b/esm/object-path-immutable.js @@ -51,37 +51,6 @@ function getKey (key) { return key } -var objectPathImmutable = function (src) { - var dest = src; - var committed = false; - - var transaction = Object.keys(api).reduce(function (proxy, prop) { - /* istanbul ignore else */ - if (typeof api[prop] === 'function') { - proxy[prop] = function () { - var args = [dest, src].concat(Array.prototype.slice.call(arguments)); - - if (committed) { - throw new Error('Cannot call ' + prop + ' after `value`') - } - - dest = api[prop].apply(null, args); - - return transaction - }; - } - - return proxy - }, {}); - - transaction.value = function () { - committed = true; - return dest - }; - - return transaction -}; - function clone (obj, createIfEmpty, assumeArray) { if (obj == null) { if (createIfEmpty) { @@ -100,13 +69,13 @@ function clone (obj, createIfEmpty, assumeArray) { return assignToObj({}, obj) } -function deepMerge (dest, src) { +function _deepMerge (dest, src) { if (dest !== src && isPlainObject(dest) && isPlainObject(src)) { var merged = {}; for (var key in dest) { if (_hasOwnProperty.call(dest, key)) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]); + merged[key] = _deepMerge(dest[key], src[key]); } else { merged[key] = dest[key]; } @@ -115,7 +84,7 @@ function deepMerge (dest, src) { for (key in src) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]); + merged[key] = _deepMerge(dest[key], src[key]); } } return merged @@ -123,7 +92,7 @@ function deepMerge (dest, src) { return src } -function changeImmutable (dest, src, path, changeCallback) { +function _changeImmutable (dest, src, path, changeCallback) { if (isNumber(path)) { path = [path]; } @@ -131,7 +100,7 @@ function changeImmutable (dest, src, path, changeCallback) { return src } if (isString(path)) { - return changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) + return _changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) } var currentPath = path[0]; @@ -147,7 +116,7 @@ function changeImmutable (dest, src, path, changeCallback) { src = src[currentPath]; } - dest[currentPath] = changeImmutable(dest[currentPath], src, path.slice(1), changeCallback); + dest[currentPath] = _changeImmutable(dest[currentPath], src, path.slice(1), changeCallback); return dest } @@ -157,7 +126,7 @@ api.set = function set (dest, src, path, value) { if (isEmpty(path)) { return value } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = value; return clonedObj }) @@ -167,7 +136,7 @@ api.update = function update (dest, src, path, updater) { if (isEmpty(path)) { return updater(clone(src)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = updater(clonedObj[finalPath]); return clonedObj }) @@ -182,7 +151,7 @@ api.push = function push (dest, src, path /*, values */) { return src.concat(values) } } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (!isArray(clonedObj[finalPath])) { clonedObj[finalPath] = values; } else { @@ -203,7 +172,7 @@ api.insert = function insert (dest, src, path, value, at) { first.push(value); return first.concat(src.slice(at)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { var arr = clonedObj[finalPath]; if (!isArray(arr)) { if (arr != null && typeof arr !== 'undefined') { @@ -223,7 +192,7 @@ api.del = function del (dest, src, path) { if (isEmpty(path)) { return undefined } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (Array.isArray(clonedObj)) { if (clonedObj[finalPath] !== undefined) { clonedObj.splice(finalPath, 1); @@ -244,7 +213,7 @@ api.assign = function assign (dest, src, path, source) { } return assignToObj(clone(src), source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source); var target = clone(clonedObj[finalPath], true); assignToObj(target, source); @@ -259,17 +228,52 @@ api.merge = function assign (dest, src, path, source) { if (isEmpty(source)) { return src } - return deepMerge(src, source) + return _deepMerge(src, source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source); - clonedObj[finalPath] = deepMerge(clonedObj[finalPath], source); + clonedObj[finalPath] = _deepMerge(clonedObj[finalPath], source); return clonedObj }) }; -module.exports = Object.keys(api).reduce(function (objectPathImmutable, method) { - objectPathImmutable[method] = api[method].bind(null, null); +function wrap (src) { + var dest = src; + var committed = false; + + var transaction = Object.keys(api).reduce(function (proxy, prop) { + /* istanbul ignore else */ + if (typeof api[prop] === 'function') { + proxy[prop] = function () { + var args = [dest, src].concat(Array.prototype.slice.call(arguments)); + + if (committed) { + throw new Error('Cannot call ' + prop + ' after `value`') + } + + dest = api[prop].apply(null, args); + + return transaction + }; + } + + return proxy + }, {}); + + transaction.value = function () { + committed = true; + return dest + }; + + return transaction +} + +var set = api.set.bind(null, null); +var update = api.update.bind(null, null); +var push = api.push.bind(null, null); +var insert = api.insert.bind(null, null); +var del = api.del.bind(null, null); +var assign = api.assign.bind(null, null); +var merge = api.merge.bind(null, null); - return objectPathImmutable -}, objectPathImmutable); +export { assign, del, insert, merge, push, set, update, wrap }; diff --git a/object-path-immutable.d.ts b/object-path-immutable.d.ts index 4d0680f..07dce08 100644 --- a/object-path-immutable.d.ts +++ b/object-path-immutable.d.ts @@ -12,7 +12,7 @@ interface WrappedObject { } interface ObjectPathImmutable { - (obj: T): WrappedObject + wrap(obj: T): WrappedObject set(src: T, path?: Path, value?: any): T push(src: T, path?: Path, value?: any): T del(src: T, path?: Path): T diff --git a/package.json b/package.json index ef55bf0..6a9dc92 100644 --- a/package.json +++ b/package.json @@ -19,25 +19,25 @@ "scripts": { "build": "rollup -c", "pretest": "standard", - "test": "npm run build && mocha test.js", + "test": "npm run build && mocha test/test.js", "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls", "coverage": "nyc npm test", "prepublish": "npm run coverage", "standard-fix": "standard --fix" }, "dependencies": { - "is-plain-object": "3.0.0" + "is-plain-object": "^3.0.0" }, "devDependencies": { - "chai": "4.2.0", - "coveralls": "3.0.6", + "chai": "^4.2.0", + "coveralls": "^3.0.6", "nyc": "^14.1.1", - "mocha": "6.2.0", - "mocha-lcov-reporter": "1.3.0", - "rollup": "1.21.4", - "rollup-plugin-commonjs": "10.1.0", - "rollup-plugin-node-resolve": "5.2.0", - "standard": "14.3.1" + "mocha": "^6.2.0", + "mocha-lcov-reporter": "^1.3.0", + "rollup": "^1.21.4", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-node-resolve": "^5.2.0", + "standard": "^14.3.1" }, "keywords": [ "deep", diff --git a/rollup.config.js b/rollup.config.js index cb74982..0fb8766 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -19,11 +19,13 @@ export default [{ file: `cjs/${pkg.name}.js`, format: 'cjs', esModule: false - } + }, + external: [ 'is-plain-object' ] }, { input: 'src/object-path-immutable.js', output: { file: `esm/${pkg.name}.js`, format: 'esm' - } + }, + external: [ 'is-plain-object' ] }] diff --git a/src/object-path-immutable.js b/src/object-path-immutable.js index 51161dc..fdcf8ce 100644 --- a/src/object-path-immutable.js +++ b/src/object-path-immutable.js @@ -50,37 +50,6 @@ function getKey (key) { return key } -var objectPathImmutable = function (src) { - var dest = src - var committed = false - - var transaction = Object.keys(api).reduce(function (proxy, prop) { - /* istanbul ignore else */ - if (typeof api[prop] === 'function') { - proxy[prop] = function () { - var args = [dest, src].concat(Array.prototype.slice.call(arguments)) - - if (committed) { - throw new Error('Cannot call ' + prop + ' after `value`') - } - - dest = api[prop].apply(null, args) - - return transaction - } - } - - return proxy - }, {}) - - transaction.value = function () { - committed = true - return dest - } - - return transaction -} - function clone (obj, createIfEmpty, assumeArray) { if (obj == null) { if (createIfEmpty) { @@ -99,13 +68,13 @@ function clone (obj, createIfEmpty, assumeArray) { return assignToObj({}, obj) } -function deepMerge (dest, src) { +function _deepMerge (dest, src) { if (dest !== src && isPlainObject(dest) && isPlainObject(src)) { var merged = {} for (var key in dest) { if (_hasOwnProperty.call(dest, key)) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]) + merged[key] = _deepMerge(dest[key], src[key]) } else { merged[key] = dest[key] } @@ -114,7 +83,7 @@ function deepMerge (dest, src) { for (key in src) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]) + merged[key] = _deepMerge(dest[key], src[key]) } } return merged @@ -122,7 +91,7 @@ function deepMerge (dest, src) { return src } -function changeImmutable (dest, src, path, changeCallback) { +function _changeImmutable (dest, src, path, changeCallback) { if (isNumber(path)) { path = [path] } @@ -130,7 +99,7 @@ function changeImmutable (dest, src, path, changeCallback) { return src } if (isString(path)) { - return changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) + return _changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) } var currentPath = path[0] @@ -146,7 +115,7 @@ function changeImmutable (dest, src, path, changeCallback) { src = src[currentPath] } - dest[currentPath] = changeImmutable(dest[currentPath], src, path.slice(1), changeCallback) + dest[currentPath] = _changeImmutable(dest[currentPath], src, path.slice(1), changeCallback) return dest } @@ -156,7 +125,7 @@ api.set = function set (dest, src, path, value) { if (isEmpty(path)) { return value } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = value return clonedObj }) @@ -166,7 +135,7 @@ api.update = function update (dest, src, path, updater) { if (isEmpty(path)) { return updater(clone(src)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = updater(clonedObj[finalPath]) return clonedObj }) @@ -181,7 +150,7 @@ api.push = function push (dest, src, path /*, values */) { return src.concat(values) } } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (!isArray(clonedObj[finalPath])) { clonedObj[finalPath] = values } else { @@ -202,7 +171,7 @@ api.insert = function insert (dest, src, path, value, at) { first.push(value) return first.concat(src.slice(at)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { var arr = clonedObj[finalPath] if (!isArray(arr)) { if (arr != null && typeof arr !== 'undefined') { @@ -222,7 +191,7 @@ api.del = function del (dest, src, path) { if (isEmpty(path)) { return undefined } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (Array.isArray(clonedObj)) { if (clonedObj[finalPath] !== undefined) { clonedObj.splice(finalPath, 1) @@ -243,7 +212,7 @@ api.assign = function assign (dest, src, path, source) { } return assignToObj(clone(src), source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source) var target = clone(clonedObj[finalPath], true) assignToObj(target, source) @@ -258,17 +227,50 @@ api.merge = function assign (dest, src, path, source) { if (isEmpty(source)) { return src } - return deepMerge(src, source) + return _deepMerge(src, source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source) - clonedObj[finalPath] = deepMerge(clonedObj[finalPath], source) + clonedObj[finalPath] = _deepMerge(clonedObj[finalPath], source) return clonedObj }) } -module.exports = Object.keys(api).reduce(function (objectPathImmutable, method) { - objectPathImmutable[method] = api[method].bind(null, null) +export function wrap (src) { + var dest = src + var committed = false + + var transaction = Object.keys(api).reduce(function (proxy, prop) { + /* istanbul ignore else */ + if (typeof api[prop] === 'function') { + proxy[prop] = function () { + var args = [dest, src].concat(Array.prototype.slice.call(arguments)) + + if (committed) { + throw new Error('Cannot call ' + prop + ' after `value`') + } + + dest = api[prop].apply(null, args) + + return transaction + } + } + + return proxy + }, {}) + + transaction.value = function () { + committed = true + return dest + } + + return transaction +} - return objectPathImmutable -}, objectPathImmutable) +export var set = api.set.bind(null, null) +export var update = api.update.bind(null, null) +export var push = api.push.bind(null, null) +export var insert = api.insert.bind(null, null) +export var del = api.del.bind(null, null) +export var assign = api.assign.bind(null, null) +export var merge = api.merge.bind(null, null) diff --git a/test.js b/test/test.js similarity index 96% rename from test.js rename to test/test.js index 2a8c521..4fc6b40 100644 --- a/test.js +++ b/test/test.js @@ -1,9 +1,7 @@ /* globals describe, it */ -'use strict' - var expect = require('chai').expect -var op = require('./') +var op = require('../') describe('set', function () { it('should set a deep key without modifying the original object', function () { @@ -506,7 +504,7 @@ describe('merge', function () { expect(newObj.a.c.f).to.be.eql({ a: 1 }) }) - it('should work with bind and if the destination is undefined', function () { + it('should work with wrap and if the destination is undefined', function () { var obj = { a: { b: 1, @@ -517,13 +515,13 @@ describe('merge', function () { } } - var newObj = op(obj).merge('a.c.f', { a: 1 }).value() + var newObj = op.wrap(obj).merge('a.c.f', { a: 1 }).value() expect(newObj.a.c.f).to.be.eql({ a: 1 }) }) }) describe('bind', function () { - it('should execute all methods on the bound object', function () { + it('should execute all methods on the wrapped object', function () { var obj = { a: { d: 1, @@ -532,7 +530,7 @@ describe('bind', function () { c: {} } - var newObj = op(obj).set('a.q', 'q').del('a.d').update('a.f', function (v) { + var newObj = op.wrap(obj).set('a.q', 'q').del('a.d').update('a.f', function (v) { return v + 1 }).value() @@ -543,14 +541,14 @@ describe('bind', function () { expect(newObj.a).to.be.eql({ f: 3, q: 'q' }) }) - it('should return the bound object if no operations made', function () { + it('should return the wrapped object if no operations made', function () { var obj = {} - expect(op(obj).value()).to.be.equal(obj) + expect(op.wrap(obj).value()).to.be.equal(obj) }) it('should throw if an operation is attempted after `value` called', function () { - var transaction = op({ + var transaction = op.wrap({ foo: 'bar', fiz: [], frob: {} diff --git a/umd/object-path-immutable.js b/umd/object-path-immutable.js index a2ec480..5609d95 100644 --- a/umd/object-path-immutable.js +++ b/umd/object-path-immutable.js @@ -1,7 +1,8 @@ -(function (factory) { - typeof define === 'function' && define.amd ? define(factory) : - factory(); -}(function () { 'use strict'; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = global || self, factory(global.objectPathImmutable = {})); +}(this, function (exports) { 'use strict'; /*! * isobject @@ -99,37 +100,6 @@ return key } - var objectPathImmutable = function (src) { - var dest = src; - var committed = false; - - var transaction = Object.keys(api).reduce(function (proxy, prop) { - /* istanbul ignore else */ - if (typeof api[prop] === 'function') { - proxy[prop] = function () { - var args = [dest, src].concat(Array.prototype.slice.call(arguments)); - - if (committed) { - throw new Error('Cannot call ' + prop + ' after `value`') - } - - dest = api[prop].apply(null, args); - - return transaction - }; - } - - return proxy - }, {}); - - transaction.value = function () { - committed = true; - return dest - }; - - return transaction - }; - function clone (obj, createIfEmpty, assumeArray) { if (obj == null) { if (createIfEmpty) { @@ -148,13 +118,13 @@ return assignToObj({}, obj) } - function deepMerge (dest, src) { + function _deepMerge (dest, src) { if (dest !== src && isPlainObject(dest) && isPlainObject(src)) { var merged = {}; for (var key in dest) { if (_hasOwnProperty.call(dest, key)) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]); + merged[key] = _deepMerge(dest[key], src[key]); } else { merged[key] = dest[key]; } @@ -163,7 +133,7 @@ for (key in src) { if (_hasOwnProperty.call(src, key)) { - merged[key] = deepMerge(dest[key], src[key]); + merged[key] = _deepMerge(dest[key], src[key]); } } return merged @@ -171,7 +141,7 @@ return src } - function changeImmutable (dest, src, path, changeCallback) { + function _changeImmutable (dest, src, path, changeCallback) { if (isNumber(path)) { path = [path]; } @@ -179,7 +149,7 @@ return src } if (isString(path)) { - return changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) + return _changeImmutable(dest, src, path.split('.').map(getKey), changeCallback) } var currentPath = path[0]; @@ -195,7 +165,7 @@ src = src[currentPath]; } - dest[currentPath] = changeImmutable(dest[currentPath], src, path.slice(1), changeCallback); + dest[currentPath] = _changeImmutable(dest[currentPath], src, path.slice(1), changeCallback); return dest } @@ -205,7 +175,7 @@ if (isEmpty(path)) { return value } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = value; return clonedObj }) @@ -215,7 +185,7 @@ if (isEmpty(path)) { return updater(clone(src)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { clonedObj[finalPath] = updater(clonedObj[finalPath]); return clonedObj }) @@ -230,7 +200,7 @@ return src.concat(values) } } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (!isArray(clonedObj[finalPath])) { clonedObj[finalPath] = values; } else { @@ -251,7 +221,7 @@ first.push(value); return first.concat(src.slice(at)) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { var arr = clonedObj[finalPath]; if (!isArray(arr)) { if (arr != null && typeof arr !== 'undefined') { @@ -271,7 +241,7 @@ if (isEmpty(path)) { return undefined } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { if (Array.isArray(clonedObj)) { if (clonedObj[finalPath] !== undefined) { clonedObj.splice(finalPath, 1); @@ -292,7 +262,7 @@ } return assignToObj(clone(src), source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source); var target = clone(clonedObj[finalPath], true); assignToObj(target, source); @@ -307,19 +277,63 @@ if (isEmpty(source)) { return src } - return deepMerge(src, source) + return _deepMerge(src, source) } - return changeImmutable(dest, src, path, function (clonedObj, finalPath) { + return _changeImmutable(dest, src, path, function (clonedObj, finalPath) { source = Object(source); - clonedObj[finalPath] = deepMerge(clonedObj[finalPath], source); + clonedObj[finalPath] = _deepMerge(clonedObj[finalPath], source); return clonedObj }) }; - module.exports = Object.keys(api).reduce(function (objectPathImmutable, method) { - objectPathImmutable[method] = api[method].bind(null, null); + function wrap (src) { + var dest = src; + var committed = false; + + var transaction = Object.keys(api).reduce(function (proxy, prop) { + /* istanbul ignore else */ + if (typeof api[prop] === 'function') { + proxy[prop] = function () { + var args = [dest, src].concat(Array.prototype.slice.call(arguments)); + + if (committed) { + throw new Error('Cannot call ' + prop + ' after `value`') + } + + dest = api[prop].apply(null, args); + + return transaction + }; + } + + return proxy + }, {}); + + transaction.value = function () { + committed = true; + return dest + }; + + return transaction + } - return objectPathImmutable - }, objectPathImmutable); + var set = api.set.bind(null, null); + var update = api.update.bind(null, null); + var push = api.push.bind(null, null); + var insert = api.insert.bind(null, null); + var del = api.del.bind(null, null); + var assign = api.assign.bind(null, null); + var merge = api.merge.bind(null, null); + + exports.assign = assign; + exports.del = del; + exports.insert = insert; + exports.merge = merge; + exports.push = push; + exports.set = set; + exports.update = update; + exports.wrap = wrap; + + Object.defineProperty(exports, '__esModule', { value: true }); }));