diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..15ad7a6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2017 Sam Gluck
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 8c05cb4..3c3e9db 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
mewt
-:seedling: Immutability in under a kilobyte
+Immutability in under one kilobyte
Made with ❤ at @outlandish
@@ -14,7 +14,11 @@
-:cookie: Under 1kb unminified. Zero dependencies.
+:seedling: Under 1kb (unminified), tiny API, zero dependencies.
+
+:+1: Makes all native array methods immutable operations.
+
+:v: Two simple methods `$set` and `$unset` for objects and arrays.
:point_right: Built for Node ES2015 environments. Use a bundler, transpiler, and Proxy polyfill as required.
@@ -42,45 +46,72 @@ var immutable = require('mewt')
## Usage
-Create an immutable instance from a JavaScript array or object:
+Create an immutable instance from a JavaScript array or object.
+
+Both objects and arrays have the `$set` and `$unset` methods.
```js
-let immutableArray = immutable([])
-let immutableObect = immutable({})
+const immutableArray = immutable([])
+const immutableObect = immutable({})
+
+immutableArray[0] = 'Van Morrison' //=> Error "array is immutable"
+immutableObect.name = 'Van Morrison' //=> Error "object is immutable"
```
### Array
+Use `$set` and `$unset` to create new array with applied change.
+
+Use all array instance methods as usual, however those that would normally return a single
+non-array value (pop, push, shift, unshift) will return an array containing the value and a new array
+(see part 2 in example below).
+
```js
const arr = immutable([])
-// all array instance methods are available
+// 1. all array instance methods are available
const arr1 = arr.concat('bubble')
- console.log(arr) //=> ['bubble']
+ console.log(arr1) //=> ['bubble']
console.log(arr1 === arr) //=> false
-// methods with non-array return value also return new
-// array via destructuring (push, pop, shift, unshift)
+// 2. methods with non-array return value (push, pop, shift, unshift)
+// also return new array, accessible via destructuring
const [val, arr2] = arr1.pop()
console.log(val) //=> 'bubble'
console.log(arr2) //=> []
console.log(arr2 === arr1) //=> false
+
+// 3. use $set and $unset to get new array with changes
+const arr3 = arr2.$set(0, 'Iggy Pop')
+
+ console.log(arr3) //=> ['Iggy Pop']
+ console.log(arr3 === arr2) //=> false
```
### Object
+Use `$set` and `$unset` to create new object with applied change.
+
```js
const obj = immutable({})
-// properties are added/updated using `$set`
-const obj1 = obj.set('album', 'Hunky Dory')
+// 1. properties are added/updated using `$set`
+const obj1 = obj.$set('album', 'Hunky Dory')
console.log(obj1) //=> {album: 'Hunky Dory'}
+ console.log(obj1 === obj) //=> false
-// properties are deleted using `$unset`
+// 2. properties are deleted using `$unset`
const obj2 = obj1.$unset('album')
console.log(obj2) //=> {}
+ console.log(obj2 === obj1) //=> false
```
+
+## Contributing
+
+All pull requests and issues welcome!
+
+If you're not sure how, check out the [great video tutorials on egghead.io](http://bit.ly/2aVzthz)!
diff --git a/index.js b/index.js
index e24db6e..5c10a70 100644
--- a/index.js
+++ b/index.js
@@ -1,22 +1,29 @@
-/** Create immutable instance of array or object.
- * @returns {Array|Object} */
+/** @returns {Array|Object} */
module.exports = function mewt (target) {
- let current = target
- , isArr = Array.isArray(target)
+ let isA = Array.isArray(target)
, multiRet = 'push pop shift unshift'
- , clone = v => isArr ? [].concat(v) : Object.assign({}, v)
+ , clone = (v, soft) => (v = isA ? [].concat(v) : Object.assign({}, v), soft ? v : mewt(v))
- let override = fn => (...args) => {
- let res = current[fn](...args)
- current = clone(current)
- return multiRet.includes(fn) ? [res, current] : current
+ let override = prop => (...args) => {
+ let res = target[prop](...args)
+ return multiRet.includes(prop) ? [res, clone(target)] : clone(res)
}
- if (!isArr && typeof target !== 'object')
+ let newObj, api = {
+ $set: (prop, val) => (newObj = clone(target, true), newObj[prop] = val, newObj),
+ $unset: prop => (newObj = clone(target, true), delete newObj[prop], newObj)
+ }
+
+ if (!isA && typeof target !== 'object')
throw new Error('mewt accepts array or object')
return new Proxy(target, {
- set: (_, prop, val) => (current = clone(current), current[prop] = val, true),
- get: (_, prop) => current[prop] && (current.hasOwnProperty(prop) ? current[prop] : override(prop))
+ set: () => {
+ throw new Error(`${isA ? 'array' : 'object'} is immutable`)
+ },
+ get: (_, prop) => {
+ if (api[prop]) return api[prop]
+ return target[prop] && (target.hasOwnProperty(prop) ? target[prop] : override(prop))
+ }
})
}
diff --git a/package.json b/package.json
index b4b9b2a..0754ba0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "mewt",
"version": "1.0.0",
- "description": "Immutability in under a kilobyte",
+ "description": "Immutability in under one kilobyte",
"main": "index.js",
"scripts": {
"test": "node test"
diff --git a/test.js b/test.js
index 0facd06..0b6bb3c 100644
--- a/test.js
+++ b/test.js
@@ -20,6 +20,36 @@ test('array', (t) => {
const n = mewt(a)
t.notEqual(a, n)
}
+ {
+ // throws on mutation
+ const a = mewt([])
+ t.throws(() => a[0] = 'Lodger', /immutable/)
+ }
+ {
+ // has $set & $unset
+ const a = mewt([])
+ t.equal(typeof a.$set, 'function')
+ t.equal(typeof a.$unset, 'function')
+ }
+ {
+ // get own property
+ const a = mewt([''])
+ t.equal(a[0], '')
+ }
+ {
+ // $set
+ const a = mewt([])
+ const n = a.$set(0, '')
+ t.deepEqual(n, [''])
+ t.notEqual(a, n)
+ }
+ {
+ // $unset
+ const a = mewt([''])
+ const n = a.$unset(0)
+ t.deepEqual(n, [])
+ t.notEqual(a, n)
+ }
{
// copyWithin
const a = mewt([1, 2])
@@ -76,7 +106,7 @@ test('array', (t) => {
// sort
const a = mewt(['a', 'b', 'c'])
const n = a.splice(0, 1)
- t.deepEqual(n, ['b', 'c'])
+ t.deepEqual(n, ['a'])
t.notEqual(a, n)
}
{
@@ -98,9 +128,36 @@ test('object', (t) => {
t.notEqual(o, n)
}
{
- //
- const o = {}
- const n = mewt(o)
+ // throws on mutation
+ const o = mewt({})
+ t.throws(() => o.track = 'The Promise', /immutable/)
+ }
+ {
+ // has $set & $unset
+ const o = mewt({})
+ t.equal(typeof o.$set, 'function')
+ t.equal(typeof o.$unset, 'function')
+ }
+ {
+ // get own property
+ const o = mewt({album: 'Aladdin Sane'})
+ t.equal(o.album, 'Aladdin Sane')
+ }
+ {
+ // $set
+ const o = mewt({})
+ const n = o.$set('album', 'Hours')
+ console.log(o.album)
+ t.equal(o.album, undefined)
+ t.equal(n.album, 'Hours')
+ t.notEqual(o, n)
+ }
+ {
+ // $unset
+ const o = mewt({album: 'Heroes'})
+ const n = o.$unset('album')
+ t.equal(o.album, 'Heroes')
+ t.equal(n.album, undefined)
t.notEqual(o, n)
}
t.end()