diff --git a/.babelrc b/.babelrc index 2bea062..c66b4ae 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,6 @@ { - "plugins": ["transform-es2015-modules-commonjs"] + "plugins": [ + "transform-class-properties", + "transform-es2015-modules-commonjs" + ] } diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 76c63f4..0000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,16 +0,0 @@ -engines: - duplication: - enabled: true - config: - languages: - - javascript - eslint: - enabled: true - fixme: - enabled: true -ratings: - paths: - - src/** -exclude_paths: -- test/ -- gulpfile.js diff --git a/.eslintrc b/.eslintrc index b5e56f7..e302c8b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -33,6 +33,7 @@ "no-cond-assign": 0, "no-unused-vars": 0, "constructor-super": 0, - "no-class-assign": 0 + "no-class-assign": 0, + "no-debugger": 0 } } diff --git a/.travis.yml b/.travis.yml index 5c2ec9d..b3e75aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,3 @@ sudo: false install: npm install -g yarn && yarn before_script: sh -c "git log | head -12" script: npm run coverage -after_success: - - coveralls < ./coverage/lcov.info - - CODECLIMATE_REPO_TOKEN=fb038884df12a473bb3cc13c8ef5323227e19c8818327926cde26e89eb7f31cb codeclimate-test-reporter < ./coverage/lcov.info diff --git a/LICENSE b/LICENSE index a2c1f4a..d2a4049 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016 Chris Barber +Copyright (c) 2016-2017 Chris Barber Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a28a73e..3a54ccd 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,15 @@ [![NPM Downloads][downloads-image]][downloads-url] [![Travis CI Build][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] -[![Code Climate][codeclimate-image]][codeclimate-url] [![Deps][david-image]][david-url] [![Dev Deps][david-dev-image]][david-dev-url] -Gawk wraps JavaScript objects and arrays making them observable. Once a -JavaScript value is gawked, you can listen for changes including deeply nested -changes. Only objects and arrays can be gawked. All other types are passed -through. +Gawk wraps JavaScript objects and arrays making them observable. Once a JavaScript object is gawked, +you can listen for changes including deeply nested changes. Only objects and arrays can be gawked. +All other types are passed through. -Gawked objects and arrays can be interacted with as if they were regular -objects/arrays. Built-in functions such as `JSON.stringify()` work as expected. +Gawked objects and arrays can be interacted with as if they were regular objects/arrays. Built-in +functions such as `JSON.stringify()` work as expected. > Note: gawk uses ES2015 proxies and thus requires Node.js 6 or newer. @@ -77,34 +75,44 @@ gawk.watch(obj, [ 'foo', 'bar' ], value => { obj.foo.bar = 'world!'; ``` -You can directly create `GawkObject` and `GawkArray` objects: +To stop watching, simply call `gawk.unwatch()` with the original listener function. ```javascript -import { GawkArray, GawkObject } from 'gawk'; +const obj = gawk({ /* ... */ }); -const obj = new GawkObject({ foo: 'bar' }); -const arr = new GawkArray('a', 'b', 'c'); +function onChange(obj, src) { + console.log('changed!'); +} + +gawk.watch(obj, onChange); + +obj.foo = 'bar'; + +gawk.unwatch(obj, onChange); + +obj.foo = 'baz'; // does not fire onChange() ``` -## Upgrading to v3 +## Upgrading to v4 + +Gawk v4 has dropped all gawk data types. You must always call `gawk()`. -Gawk v3 has dropped all gawk data types except `GawkArray` and `GawkObject`. +Change all `new GawkObject()` calls to `gawk({})` and `new GawkArray()` to `gawk([])`. -Since Gawk v3 uses ES6 Proxies, you no longer need to call `obj.get()`, -`obj.set()`, `obj.delete()`, etc. +Since Gawk v3 and newer uses ES6 Proxies, you no longer need to call `obj.get()`, `obj.set()`, +`obj.delete()`, etc. -Methods `obj.watch()`, `obj.merge()`, and `obj.mergeDeep()` have moved to -`gawk.watch()`, `gawk.merge()`, and `gawk.mergeDeep()`. The first argument must -be a gawk object. +Methods `obj.watch()`, `obj.merge()`, and `obj.mergeDeep()` have moved to `gawk.watch()`, +`gawk.merge()`, and `gawk.mergeDeep()`. The first argument must be a gawk object. -Gawk v3 no longer hashes values. This means speed. Gawk v3 is about 19 times -faster than v1 and v2. +Starting in v3, Gawk no longer hashes values. This means speed. Gawk v3+ is about 19 times faster +than v1 and v2. ## License (The MIT License) -Copyright (c) 2016 Chris Barber +Copyright (c) 2016-2017 Chris Barber Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -132,8 +140,6 @@ THE SOFTWARE. [travis-url]: https://travis-ci.org/cb1kenobi/gawk [coveralls-image]: https://img.shields.io/coveralls/cb1kenobi/gawk/master.svg [coveralls-url]: https://coveralls.io/r/cb1kenobi/gawk -[codeclimate-image]: https://img.shields.io/codeclimate/github/cb1kenobi/gawk.svg -[codeclimate-url]: https://codeclimate.com/github/cb1kenobi/gawk [david-image]: https://img.shields.io/david/cb1kenobi/gawk.svg [david-url]: https://david-dm.org/cb1kenobi/gawk [david-dev-image]: https://img.shields.io/david/dev/cb1kenobi/gawk.svg diff --git a/package.json b/package.json index 4e38208..410ce4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gawk", - "version": "3.4.0", + "version": "4.0.0", "description": "Observable JavaScript object model", "main": "./dist/index.js", "author": "Chris Barber (https://github.com/cb1kenobi)", @@ -27,10 +27,10 @@ }, "devDependencies": { "babel-eslint": "^7.2.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", "chai": "^3.5.0", - "codeclimate-test-reporter": "^0.4.1", - "coveralls": "^2.12.0", + "coveralls": "^2.13.0", "del": "^2.2.2", "esdoc-es7-plugin": "^0.0.3", "gulp": "^3.9.1", @@ -44,7 +44,7 @@ "gulp-load-plugins": "^1.5.0", "gulp-mocha": "^3.0.1", "gulp-plumber": "^1.1.0", - "gulp-sourcemaps": "^2.4.1", + "gulp-sourcemaps": "^2.5.1", "sinon": "^2.1.0", "sinon-chai": "^2.9.0" }, diff --git a/src/index.js b/src/index.js index b3c1687..54947bd 100644 --- a/src/index.js +++ b/src/index.js @@ -2,202 +2,183 @@ if (!Error.prepareStackTrace) { require('source-map-support/register'); } -/** - * Creates a gawk object that wraps the specified object. - * - * @param {*} value - A value to gawk. - * @param {GawkArray|GawkObject} [parent] - The parent gawk object. - * @returns {GawkArray|GawkObject|*} - */ -export default function gawk(value, parent) { - if (parent && !(parent instanceof GawkArray) && !(parent instanceof GawkObject)) { - throw new TypeError('Expected parent to be a GawkArray or GawkObject'); - } +export class Gawk { + /** + * A list of all the gawk object's parents. These parents are notified when a change occurs. + * @type {Set} + */ + parents = new Set; - if (!value || typeof value !== 'object' || value instanceof Date) { - return value; - } + /** + * A map of listener functions to call invoke when a change occurs. The associated key value is + * the optional filter to apply to the listener. + * @type {Map} + */ + listeners = new Map; - let gawked; - if (value instanceof GawkArray || value instanceof GawkObject) { - if (value === parent) { - throw new Error('The parent must not be the same object as the value'); - } - gawked = value; + /** + * A map of listener functions to the last known hash of the stringified value. This is used to + * detect if a filtered watch should be notified. + * @type {WeakMap} + */ + previous = new WeakMap; - } else if (Array.isArray(value)) { - gawked = new GawkArray(); - gawked.push.apply(gawked, value); + /** + * A list of child objects that are modified while paused. + * @type {Set} + */ + queue = null; - } else { - gawked = new GawkObject(value); + /** + * Creates the internal Gawk state. + * + * @param {Object} instance - The object being gawked. + */ + constructor(instance) { + this.instance = instance; } - if (parent) { - gawked.__gawk__.parents.add(parent); + /** + * Dispatches change notifications to the listeners. + */ + pause() { + if (!this.queue) { + this.queue = new Set; + } } - return gawked; -} + /** + * Unpauses the gawk notifications and sends out any pending notifications. + */ + resume() { + if (this.queue) { + const queue = this.queue; + this.queue = null; + for (const instance of queue) { + this.notify(instance); + } + } + } -export { gawk as gawk }; + /** + * Dispatches change notifications to the listeners. + * + * @param {Object|Array} [source] - The gawk object that was modified. + */ + notify(source) { + if (source === undefined) { + source = this.instance; + } -/** - * Internal function to wire up all gawk instances. Normally this would go in - * a base class that GawkObject and GawkArray extend, however JavaScript doesn't - * support multiple inheritance and so instead we tack the gawk functions onto - * the specified instance. - * - * @param {GawkArray|GawkObject} - */ -function gawkify(instance) { - const internal = Object.defineProperty(instance, '__gawk__', { - value: { - /** - * A list of all the gawk object's parents. These parents are - * notified when a change occurs. - * @type {Set} - */ - parents: new Set, - - /** - * A map of listener functions to call invoke when a change occurs. The - * associated key value is the optional filter to apply to the listener. - * @type {Map} - */ - listeners: new Map, - - /** - * A map of listener functions to the last known hash of the stringified - * value. This is used to detect if a filtered watch should be notified. - * @type {WeakMap} - */ - previous: new WeakMap, - - /** - * A list of child objects that are modified while paused. - * @type {Set} - */ - queue: null, - - /** - * Dispatches change notifications to the listeners. - */ - pause: () => { - if (!internal.queue) { - internal.queue = new Set; - } - }, + if (this.queue) { + this.queue.add(this.instance); + return; + } - /** - * Unpauses the gawk notifications and sends out any pending - * notifications. - */ - resume: () => { - if (internal.queue) { - const queue = internal.queue; - internal.queue = null; - for (const instance of queue) { - internal.notify(instance); + // notify all of this object's listeners + for (const [ listener, filter ] of this.listeners) { + if (filter) { + let obj = this.instance; + let found = true; + + // find the value we're interested in + for (let i = 0, len = filter.length; obj && typeof obj === 'object' && i < len; i++) { + if (!obj.hasOwnProperty(filter[i])) { + found = false; + obj = undefined; + break; } + obj = obj[filter[i]]; } - }, - /** - * Dispatches change notifications to the listeners. - * - * @param {GawkArray|GawkObject} [source] - The gawk object that was - * modified. - */ - notify: (source = instance) => { - if (internal.queue) { - internal.queue.add(instance); - return; + // compute the hash of the stringified value + const str = JSON.stringify(obj) || ''; + let hash = 5381; + let i = str.length; + while (i) { + hash = (hash * 33) ^ str.charCodeAt(--i); } + hash = hash >>> 0; - // notify all of this object's listeners - for (const [ listener, filter ] of internal.listeners) { - if (filter) { - let obj = instance; - let found = true; - - // find the value we're interested in - for (let i = 0, len = filter.length; obj && typeof obj === 'object' && i < len; i++) { - if (!obj.hasOwnProperty(filter[i])) { - found = false; - obj = undefined; - break; - } - obj = obj[filter[i]]; - } - - // compute the hash of the stringified value - const str = JSON.stringify(obj) || ''; - let hash = 5381; - let i = str.length; - while (i) { - hash = (hash * 33) ^ str.charCodeAt(--i); - } - hash = hash >>> 0; - - // check if the value changed - if ((found || internal.previous.has(listener)) && hash !== internal.previous.get(listener)) { - listener(obj, source); - } - - internal.previous.set(listener, hash); - } else { - listener(instance, source); - } + // check if the value changed + if ((found || this.previous.has(listener)) && hash !== this.previous.get(listener)) { + listener(obj, source); } - // notify all of this object's parents - for (const parent of internal.parents) { - parent.__gawk__.notify(source); - } + this.previous.set(listener, hash); + } else { + listener(this.instance, source); } } - }).__gawk__; + + // notify all of this object's parents + for (const parent of this.parents) { + parent.__gawk__.notify(source); + } + } } /** - * Gawk class to wrap arrays. + * Determines if the specified variable is gawked. * - * @extends {Array} + * @param {*} it - The variable to check. + * @returns {Boolean} */ -export class GawkArray extends Array { - /** - * Constructs the array instance. - * - * @param {Number|*} [...args] - When argument is a number, sets the initial - * length of the array. When there are one or more arguments or the first - * argument is not a number, it populates the array upon initialization. - */ - constructor(...args) { - super(); +export function isGawked(it) { + return it && typeof it === 'object' && it.__gawk__ instanceof Gawk; +} + +/** + * Creates a gawk object that wraps the specified object. + * + * @param {*} value - A value to gawk. + * @param {Array|Object} [parent] - The parent gawk object. + * @returns {Array|Object|*} + */ +export default function gawk(value, parent) { + if (parent !== undefined && (typeof parent !== 'object' || !(parent.__gawk__ instanceof Gawk))) { + throw new TypeError('Expected parent to be gawked'); + } - const instance = new Proxy(this, { + // only objects can be gawked + if (!value || typeof value !== 'object') { + return value; + } + + let gawked; + + if (value.__gawk__ instanceof Gawk) { + // already gawked + if (value === parent) { + throw new Error('The parent must not be the same object as the value'); + } + gawked = value; + } else { + // gawk it! + gawked = new Proxy(value, { set: (target, prop, value) => { if (prop === '__gawk__') { - throw new Error('"__gawk__" is read-only'); + throw new Error('Cannot override property \'__gawk__\''); } - // console.log('SETTING ' + prop); + // console.log('setting', prop, value); - const existing = target[prop]; - const changed = existing !== value; + let changed = true; - if (!isNaN(parseInt(prop))) { - if (existing instanceof GawkArray || existing instanceof GawkObject) { - existing.__gawk__.parents.delete(instance); + if (target.hasOwnProperty(prop)) { + changed = target[prop] !== value; + if (isGawked(target[prop])) { + target[prop].__gawk__.parents.delete(gawked); + } + if (!Array.isArray(target) || prop !== 'length') { + delete target[prop]; } - target[prop] = gawk(value, instance); - } else { - target[prop] = value; } + target[prop] = gawk(value, gawked); + if (changed) { - target.__gawk__.notify(); + gawked.__gawk__.notify(); } return true; @@ -205,205 +186,130 @@ export class GawkArray extends Array { deleteProperty: (target, prop) => { if (prop === '__gawk__') { - throw new Error('"__gawk__" is read-only'); + throw new Error('Cannot delete property \'__gawk__\''); } - // console.log('DELETING ' + prop); + // console.log('deleting', prop, target[prop]); + + let result = true; - let exists = true; - if (!isNaN(parseInt(prop))) { + if (target.hasOwnProperty(prop)) { const value = target[prop]; - if (value instanceof GawkArray || value instanceof GawkObject) { - value.__gawk__.parents.delete(instance); + if (isGawked(value)) { + value.__gawk__.parents.delete(gawked); } - exists = target[prop] !== undefined; - } - const result = delete target[prop]; - if (exists && result) { - instance.__gawk__.notify(); + result = delete target[prop]; + if (result) { + gawked.__gawk__.notify(); + } } return result; } }); - gawkify(instance); - - instance.push.apply(instance, args.length === 1 && typeof args[0] === 'number' ? args : args.map(arg => gawk(arg, instance))); - - return instance; - } - - /** - * Removes the last element of this array. - * - * @returns {*} - * @access public - */ - pop() { - this.__gawk__.pause(); - const item = super.pop(); - this.__gawk__.resume(); - return item; - } - - /** - * Removes the first element of this array. - * - * @returns {*} - * @access public - */ - shift() { - this.__gawk__.pause(); - const item = super.shift(); - this.__gawk__.resume(); - return item; - } - - /** - * Inserts or removes items from this array. - * - * @param {Number} [start] - The starting index. - * @param {Number} [deleteCount] - The number of items to delete. If not - * specified, but `start` is, then it defaults to the length of this array. - * @param {*} [...items] - Zero or more items to insert at the specified - * `start` index. - * @returns {GawkArray} - * @access public - */ - splice(start, deleteCount, ...items) { - this.__gawk__.pause(); - - if (start !== undefined && deleteCount === undefined) { - deleteCount = this.length - start; - } - - const arr = super.splice(start, deleteCount, ...items); - for (let i = start + items.length; i < this.length; i++) { - if (this[i] && typeof this[i] === 'object') { - this[i] = gawk(this[i], this); - } - } + Object.defineProperty(gawked, '__gawk__', { + value: new Gawk(gawked) + }); - for (const item of arr) { - if (item instanceof GawkArray || item instanceof GawkObject) { - item.__gawk__.parents.delete(this); + // gawk any object properties + for (const key of Object.getOwnPropertyNames(gawked)) { + if (key !== '__gawk__' && gawked[key] && typeof gawked[key] === 'object') { + gawked[key] = gawk(gawked[key], gawked); } } - this.__gawk__.resume(); - return arr; - } - - /** - * Adds zero or more items to the beginning of this array. - * - * @param {*} [...items] - The items to add. - * @returns {Number} The new length - * @access public - */ - unshift(...items) { - this.__gawk__.pause(); - const len = super.unshift.apply(this, items.map(item => gawk(item, this))); - this.__gawk__.resume(); - return len; - } -} - -/** - * Gawk class to wrap objects. - */ -export class GawkObject { - /** - * Constructs a new GawkObject instance. - * - * @param {Object|GawkObject} [obj] - An object to copy into this instance. - */ - constructor(obj) { - if (obj === undefined || obj === null) { - obj = null; - } else if (typeof obj !== 'object' || Array.isArray(obj)) { - throw new TypeError('Expected obj to be an object or GawkObject'); - } - - // we create a proxy that allows us to listen for when properties change - const instance = new Proxy(this, { - set: (target, prop, value) => { - if (prop === '__gawk__') { - throw new Error('"__gawk__" is read-only'); - } - - // console.log('SETTING ' + prop); - - const changed = !target.hasOwnProperty(prop) || target[prop] !== value; - delete target[prop]; - target[prop] = gawk(value, instance); - - if (changed) { - target.__gawk__.notify(target); - } - - return true; - }, - - deleteProperty: (target, prop) => { - if (prop === '__gawk__') { - throw new Error('"__gawk__" is read-only'); - } - - // console.log('DELETING ' + prop); - - const value = target[prop]; - if (value instanceof GawkArray || value instanceof GawkObject) { - value.__gawk__.parents.delete(target); - } + if (Array.isArray(value)) { + // some array functions do not invoke the delete handler, so we need to override the + // method and do it ourselves + const origPop = value.pop; + const origShift = value.shift; + const origSplice = value.splice; + const origUnshift = value.unshift; + + Object.defineProperties(value, { + pop: { + configurable: true, + value: function pop() { + this.__gawk__.pause(); + const item = origPop.call(this); + this.__gawk__.resume(); + return item; + } + }, + + shift: { + configurable: true, + value: function shift() { + this.__gawk__.pause(); + const item = origShift.call(this); + this.__gawk__.resume(); + return item; + } + }, - const exists = target.hasOwnProperty(prop); - const result = delete target[prop]; - if (exists && result) { - instance.__gawk__.notify(); - } + splice: { + configurable: true, + value: function splice(start, deleteCount, ...items) { + this.__gawk__.pause(); - return result; - } - }); + if (start !== undefined && deleteCount === undefined) { + deleteCount = this.length - start; + } - gawkify(instance); + const arr = origSplice.call(this, start, deleteCount, ...items); + for (let i = start + items.length; i < this.length; i++) { + if (this[i] && typeof this[i] === 'object') { + this[i] = gawk(this[i], this); + } + } - // if we have an object argument... - if (obj) { - // copy the props to our instance - for (const key of Object.keys(obj)) { - instance[key] = gawk(obj[key], instance); - } + for (const item of arr) { + if (isGawked(item)) { + item.__gawk__.parents.delete(this); + } + } - // copy all listeners too - if (obj instanceof GawkObject) { - for (const [ listener, filter ] of obj.__gawk__.listeners) { - this.__gawk__.listeners.set(listener, filter); + this.__gawk__.resume(); + return arr; + } + }, + + unshift: { + configurable: true, + value: function unshift(...items) { + this.__gawk__.pause(); + const len = origUnshift.apply(this, items.map(item => gawk(item, this))); + this.__gawk__.resume(); + return len; + } } - } + }); } + } - return instance; + if (parent) { + gawked.__gawk__.parents.add(parent); } + + return gawked; } +export { gawk as gawk }; + /** - * Adds a listener to be called when the specified object or any of its - * properties/elements are changed. + * Adds a listener to be called when the specified object or any of its properties/elements are + * changed. * - * @param {Object|GawkObject|Array|GawkArray} subject - The object to watch. - * @param {String|Array.} [filter] - A property name or array of nested - * properties to watch. + * @param {Object|Array} subject - The object to watch. + * @param {String|Array.} [filter] - A property name or array of nested properties to watch. * @param {Function} listener - The function to call when something changes. - * @returns {GawkObject|GawkArray} Returns a gawked object or array depending on - * the input object. + * @returns {Object|Array} Returns a gawked object or array depending on the input object. */ gawk.watch = function watch(subject, filter, listener) { - if (!(subject instanceof GawkArray) && !(subject instanceof GawkObject)) { - throw new TypeError('Expected source to be a GawkArray or GawkObject'); + if (!isGawked(subject)) { + throw new TypeError('Expected subject to be gawked'); } if (typeof filter === 'function') { @@ -415,7 +321,7 @@ gawk.watch = function watch(subject, filter, listener) { if (typeof filter === 'string') { filter = [ filter ]; } else if (!Array.isArray(filter)) { - throw new TypeError('Expected filter to be a stirng or array of strings'); + throw new TypeError('Expected filter to be a string or array of strings'); } } @@ -431,14 +337,13 @@ gawk.watch = function watch(subject, filter, listener) { /** * Removes a listener from the specified gawked object. * - * @param {Object|GawkObject|Array|GawkArray} subject - The object to unwatch. + * @param {Object|Array} subject - The object to unwatch. * @param {Function} [listener] - The function to call when something changes. - * @returns {GawkObject|GawkArray} Returns a gawked object or array depending on - * the input object. + * @returns {Object|Array} Returns a gawked object or array depending on the input object. */ gawk.unwatch = function unwatch(subject, listener) { - if (!(subject instanceof GawkArray) && !(subject instanceof GawkObject)) { - throw new TypeError('Expected source to be a GawkArray or GawkObject'); + if (!isGawked(subject)) { + throw new TypeError('Expected subject to be gawked'); } if (listener) { @@ -459,16 +364,16 @@ gawk.unwatch = function unwatch(subject, listener) { }; /** - * Mixes an array of objects or GawkObjects into this GawkObject. + * Mixes an array of objects or gawked objects into the specified gawked object. * - * @param {Array} objs - An array of objects or GawkObjects. + * @param {Array.} objs - An array of objects or gawked objects. * @param {Boolean} [deep=false] - When true, mixes subobjects into each other. - * @returns {GawkObject} + * @returns {Object} */ function mix(objs, deep) { const gobj = gawk(objs.shift()); - if (!(gobj instanceof GawkObject)) { - throw new TypeError('Expected destination to be a GawkObject'); + if (!isGawked(gobj) || Array.isArray(gobj)) { + throw new TypeError('Expected destination to be a gawked object'); } if (!objs.length) { @@ -478,31 +383,33 @@ function mix(objs, deep) { // validate the objects are good for (const obj of objs) { if (!obj || typeof obj !== 'object' || Array.isArray(obj)) { - throw new TypeError('Expected source to be an object or GawkObject'); + throw new TypeError('Expected merge source to be an object'); } } - // we need to detach the parent and all listeners so that they will be - // notified after everything has been merged + // we need to detach the parent and all listeners so that they will be notified after everything + // has been merged gobj.__gawk__.pause(); /** - * Mix an object or GawkObject into a GawkObject. - * @param {GawkObject} gobj + * Mix an object or gawked object into a gawked object. + * @param {Object} gobj * @param {Object} src */ const mixer = (gobj, src) => { - for (const key of Object.keys(src)) { + for (const key of Object.getOwnPropertyNames(src)) { + if (key === '__gawk__') { + continue; + } + const srcValue = src[key]; - if (deep && srcValue !== null && typeof srcValue === 'object' && - !Array.isArray(srcValue) && !(srcValue instanceof GawkArray) - ) { - if (!(gobj[key] instanceof GawkObject)) { + if (deep && srcValue !== null && typeof srcValue === 'object' && !Array.isArray(srcValue)) { + if (!isGawked(gobj[key])) { gobj[key] = gawk({}, gobj); } mixer(gobj[key], srcValue); - } else if (gobj[key] instanceof GawkArray && Array.isArray(srcValue)) { + } else if (Array.isArray(gobj[key]) && Array.isArray(srcValue)) { // overwrite destination with new values gobj[key].splice(0, gobj[key].length, ...srcValue); } else { @@ -521,24 +428,22 @@ function mix(objs, deep) { } /** - * Performs a shallow merge of one or more objects or GawkObjects into the - * specified gawk object. + * Performs a shallow merge of one or more objects into the specified gawk object. * - * @param {Object|GawkObject} gobj - The destination GawkObject. - * @param {...Object|GawkObject} objs - One or more objects to merge in. - * @returns {GawkObject} + * @param {Object} gobj - The destination gawked object. + * @param {...Object} objs - One or more objects to merge in. + * @returns {Object} */ gawk.merge = function merge(...objs) { return mix(objs); }; /** - * Performs a deep merge of one or more objects or GawkObjects into the - * specified gawk object. + * Performs a deep merge of one or more objects into the specified gawk object. * - * @param {Object|GawkObject} gobj - The destination GawkObject. - * @param {...Object|GawkObject} objs - One or more objects to deeply merge in. - * @returns {GawkObject} + * @param {Object} gobj - The destination gawked object. + * @param {...Object} objs - One or more objects to deeply merge in. + * @returns {Object} */ gawk.mergeDeep = function mergeDeep(...objs) { return mix(objs, true); diff --git a/test/test-array.js b/test/test-array.js index 2233254..9db1752 100644 --- a/test/test-array.js +++ b/test/test-array.js @@ -1,349 +1,341 @@ -import gawk, { GawkArray, GawkObject } from '../src/index'; - -describe('GawkArray', () => { - describe('gawk()', () => { - it('should gawk empty array', () => { - const arr = []; - const garr = gawk(arr); - expect(garr).to.be.an.instanceof(GawkArray); - expect(garr.length).to.equal(0); - expect(garr).to.be.an.array; - expect(garr).to.have.lengthOf(0); - expect(garr).to.deep.equal(arr); - }); +import gawk, { Gawk, isGawked } from '../src/index'; + +import { EventEmitter } from 'events'; + +describe('gawk() array', () => { + it('should gawk empty array', () => { + const arr = []; + const garr = gawk(arr); + expect(isGawked(garr)).to.be.true; + expect(garr).to.be.an.Array; + expect(garr.length).to.equal(0); + expect(garr).to.have.lengthOf(0); + expect(garr).to.deep.equal(arr); + }); - it('should gawk an array of mixed values', () => { - const arr = ['a', 1, null, undefined, NaN, ['b', 2], function () {}]; - const garr = gawk(arr); - expect(garr).to.be.an.instanceof(GawkArray); - expect(garr.length).to.equal(7); - expect(garr).to.be.an.array; - expect(garr).to.have.lengthOf(7); - expect(garr).to.deep.equal(arr); - }); + it('should gawk an array of mixed values', () => { + const arr = ['a', 1, null, undefined, NaN, ['b', 2], function () {}]; + const garr = gawk(arr); + expect(isGawked(garr)).to.be.true; + expect(garr).to.be.an.Array; + expect(garr.length).to.equal(7); + expect(garr).to.have.lengthOf(7); + expect(garr).to.deep.equal(arr); + }); - it('should gawk an array object', () => { - const arr = new Array('a', 1, null, undefined, NaN, ['b', 2], function () {}); - const garr = gawk(arr); - expect(garr).to.be.an.instanceof(GawkArray); - expect(garr.length).to.equal(7); - expect(garr).to.be.an.array; - expect(garr).to.have.lengthOf(7); - expect(garr).to.deep.equal(arr); - }); + it('should gawk an array object', () => { + const arr = new Array('a', 1, null, undefined, NaN, ['b', 2], function () {}); + const garr = gawk(arr); + expect(isGawked(garr)).to.be.true; + expect(garr).to.be.an.Array; + expect(garr.length).to.equal(7); + expect(garr).to.have.lengthOf(7); + expect(garr).to.deep.equal(arr); + }); +}); - it('should create a gawk object without an explicit value', () => { - const garr = new GawkArray; - expect(garr).to.deep.equal([]); - }); +describe('built-ins', () => { + it('should support toString()', () => { + const garr = gawk(['a', null, undefined, 123, 3.14, NaN, Infinity]); + expect(garr.toString()).to.equal('a,,,123,3.14,NaN,Infinity'); }); - describe('new GawkArray()', () => { - it('should not allow __gawk__ to be set', () => { - const gobj = new GawkArray; - expect(gobj).to.have.property('__gawk__'); - expect(() => { - gobj.__gawk__ = 'foo'; - }).to.throw(Error); - }); + it('should support valueOf()', () => { + const arr = ['a', null, undefined, 123, 3.14, NaN, Infinity]; + const garr = gawk(arr); + expect(garr.valueOf()).to.deep.equal(arr); + }); +}); - it('should not allow __gawk__ to be deleted', () => { - const gobj = new GawkArray; - expect(gobj).to.have.property('__gawk__'); - expect(() => { - delete gobj.__gawk__; - }).to.throw(Error); - }); +describe('length', () => { + it('should have correct length', () => { + expect(gawk([]).length).to.equal(0); + expect(gawk(['a']).length).to.equal(1); + expect(gawk(['a', undefined]).length).to.equal(2); + expect(gawk(['a', undefined, 'b']).length).to.equal(3); }); +}); - describe('toString()', () => { - it('should support toString()', () => { - const garr = gawk(['a', null, undefined, 123, 3.14, NaN, Infinity]); - expect(garr.toString()).to.equal('a,,,123,3.14,NaN,Infinity'); - }); +describe('get by index', () => { + it('should get the gawked value at a valid index', () => { + const garr = gawk(['a']); + const str = garr[0]; + expect(str).to.be.a.String; + expect(str).to.equal('a'); }); - describe('valueOf()', () => { - it('should support valueOf()', () => { - const arr = ['a', null, undefined, 123, 3.14, NaN, Infinity]; - const garr = gawk(arr); - expect(garr.valueOf()).to.deep.equal(arr); - }); + it('should get the gawked value at an invalid index', () => { + const garr = gawk(['a']); + expect(garr[1]).to.be.undefined; }); +}); - describe('length', () => { - it('should have correct length', () => { - expect(gawk([]).length).to.equal(0); - expect(gawk(['a']).length).to.equal(1); - expect(gawk(['a', undefined]).length).to.equal(2); - expect(gawk(['a', undefined, 'b']).length).to.equal(3); - }); +describe('set by index', () => { + it('should set a value at the specified indices', () => { + const garr = gawk(['a']); + garr[0] = 'b'; + garr[1] = 'c'; + expect(garr).to.deep.equal(['b', 'c']); }); +}); - describe('get by index', () => { - it('should get the gawked value at a valid index', () => { - const garr = gawk(['a']); - const str = garr[0]; - expect(str).to.be.an.string; - expect(str).to.equal('a'); - }); +describe('concat()', () => { + it('should concat a gawked array and an item', () => { + const garr = gawk(['a']); + const garr2 = garr.concat('b'); + expect(garr2).to.deep.equal(['a', 'b']); + }); - it('should get the gawked value at an invalid index', () => { - const garr = gawk(['a']); - expect(garr[1]).to.be.undefined; - }); + it('should concat a gawked array and multiple items', () => { + const garr = gawk(['a']); + const garr2 = garr.concat('b', 'c'); + expect(garr2).to.deep.equal(['a', 'b', 'c']); }); - describe('set by index', () => { - it('should set a value at the specified indices', () => { - const garr = gawk(['a']); - garr[0] = 'b'; - garr[1] = 'c'; - expect(garr).to.deep.equal(['b', 'c']); - }); + it('should concat a gawked array and an array', () => { + const garr = gawk(['a']); + const garr2 = garr.concat(['b', 'c']); + expect(garr2).to.deep.equal(['a', 'b', 'c']); }); - describe('concat()', () => { - it('should concat a GawkArray and an item', () => { - const garr = gawk(['a']); - const garr2 = garr.concat('b'); - expect(garr2).to.deep.equal(['a', 'b']); - }); + it('should concat two gawked arrays', () => { + const garr = gawk(['a']); + const garr2 = garr.concat(gawk(['b', 'c'])); + expect(garr2).to.deep.equal(['a', 'b', 'c']); + }); +}); - it('should concat a GawkArray and multiple items', () => { - const garr = gawk(['a']); - const garr2 = garr.concat('b', 'c'); - expect(garr2).to.deep.equal(['a', 'b', 'c']); - }); +describe('fill()', () => { + it('should fill an array with objects', () => { + const garr = gawk(['a', 'b', 'c']); + const obj = { x: 1 }; + garr.fill(obj); + expect(garr).to.deep.equal([obj, obj, obj]); + expect(isGawked(garr[0])).to.be.true; + expect(garr[0].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[1])).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[2])).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + }); - it('should concat a GawkArray and an array', () => { - const garr = gawk(['a']); - const garr2 = garr.concat(['b', 'c']); - expect(garr2).to.deep.equal(['a', 'b', 'c']); - }); + it('should fill an array with objects with start', () => { + const garr = gawk(['a', 'b', 'c']); + const obj = { x: 1 }; + garr.fill(obj, 1); + expect(garr).to.deep.equal(['a', obj, obj]); + expect(isGawked(garr[1])).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[2])).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + }); - it('should concat two GawkArrays', () => { - const garr = gawk(['a']); - const garr2 = garr.concat(gawk(['b', 'c'])); - expect(garr2).to.deep.equal(['a', 'b', 'c']); - }); + it('should fill an array with objects with start and end', () => { + const garr = gawk(['a', 'b', 'c', 'd', 'e']); + const obj = { x: 1 }; + garr.fill(obj, 1, 4); + expect(garr).to.deep.equal(['a', obj, obj, obj, 'e']); + expect(isGawked(garr[1])).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[2])).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[3])).to.be.true; + expect(garr[3].__gawk__.parents.has(garr)).to.be.true; }); - describe('fill()', () => { - it('should fill an array with objects', () => { - const garr = gawk(['a', 'b', 'c']); - const obj = { x: 1 }; - garr.fill(obj); - expect(garr).to.deep.equal([obj, obj, obj]); - expect(garr[0]).to.be.instanceof(GawkObject); - expect(garr[0].__gawk__.parents.has(garr)).to.be.true; - expect(garr[1]).to.be.instanceof(GawkObject); - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2]).to.be.instanceof(GawkObject); - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - }); + it('should detach existing gawk objects', () => { + const gobj = gawk({}); + const garr = gawk([gobj, 'a', 'b']); + expect(gobj.__gawk__.parents.has(garr)).to.be.true; + const obj = { x: 1 }; + garr.fill(obj); + expect(garr).to.deep.equal([obj, obj, obj]); + expect(isGawked(garr[0])).to.be.true; + expect(garr[0].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[1])).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(isGawked(garr[2])).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + expect(gobj.__gawk__.parents.has(garr)).to.be.false; + }); +}); - it('should fill an array with objects with start', () => { - const garr = gawk(['a', 'b', 'c']); - const obj = { x: 1 }; - garr.fill(obj, 1); - expect(garr).to.deep.equal(['a', obj, obj]); - expect(garr[1]).to.be.instanceof(GawkObject); - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2]).to.be.instanceof(GawkObject); - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - }); +describe('pop()', () => { + it('should remove an element from the end of the array', () => { + const garr = gawk(['a', 'b']); + const str = garr.pop(); + expect(str).to.equal('b'); + expect(garr.length).to.equal(1); + expect(garr).to.deep.equal(['a']); + }); - it('should fill an array with objects with start and end', () => { - const garr = gawk(['a', 'b', 'c', 'd', 'e']); - const obj = { x: 1 }; - garr.fill(obj, 1, 4); - expect(garr).to.deep.equal(['a', obj, obj, obj, 'e']); - expect(garr[1]).to.be.instanceof(GawkObject); - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2]).to.be.instanceof(GawkObject); - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - expect(garr[3]).to.be.instanceof(GawkObject); - expect(garr[3].__gawk__.parents.has(garr)).to.be.true; - }); + it('should return undefined when popping an empty array', () => { + const garr = gawk([]); + const str = garr.pop(); + expect(str).to.be.undefined; + expect(garr.length).to.equal(0); + }); - it('should detach existing gawk objects', () => { - const gobj = gawk({}); - const garr = gawk([gobj, 'a', 'b']); - expect(gobj.__gawk__.parents.has(garr)).to.be.true; - const obj = { x: 1 }; - garr.fill(obj); - expect(garr).to.deep.equal([obj, obj, obj]); - expect(garr[0]).to.be.instanceof(GawkObject); - expect(garr[0].__gawk__.parents.has(garr)).to.be.true; - expect(garr[1]).to.be.instanceof(GawkObject); - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2]).to.be.instanceof(GawkObject); - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - expect(gobj.__gawk__.parents.has(garr)).to.be.false; - }); + it('should detach popped gawk object', () => { + const gobj = gawk({}); + const garr = gawk([gobj]); + expect(gobj.__gawk__.parents.has(garr)).to.be.true; + const gobj2 = garr.pop(); + expect(gobj2).to.equal(gobj); + expect(gobj.__gawk__.parents.has(garr)).to.be.false; }); +}); - describe('pop()', () => { - it('should remove an element from the end of the array', () => { - const garr = gawk(['a', 'b']); - const str = garr.pop(); - expect(str).to.equal('b'); - expect(garr.length).to.equal(1); - expect(garr).to.deep.equal(['a']); - }); +describe('push()', () => { + it('should return 0 if not adding any elements', () => { + const garr = gawk([]); + const n = garr.push(); + expect(n).to.equal(0); + expect(garr).to.have.lengthOf(0); + }); - it('should return undefined when popping an empty array', () => { - const garr = gawk([]); - const str = garr.pop(); - expect(str).to.be.undefined; - expect(garr.length).to.equal(0); - }); + it('should add a new element', () => { + const garr = gawk([]); + let n = garr.push('a'); + expect(n).to.equal(1); + expect(garr).to.have.lengthOf(1); + expect(garr).to.deep.equal(['a']); + + n = garr.push('b'); + expect(n).to.equal(2); + expect(garr).to.have.lengthOf(2); + expect(garr).to.deep.equal(['a', 'b']); + }); - it('should detach popped gawk object', () => { - const gobj = gawk({}); - const garr = gawk([gobj]); - expect(gobj.__gawk__.parents.has(garr)).to.be.true; - const gobj2 = garr.pop(); - expect(gobj2).to.equal(gobj); - expect(gobj.__gawk__.parents.has(garr)).to.be.false; - }); + it('should add multiple new elements', () => { + const garr = gawk([]); + let n = garr.push('a', { foo: 'bar' }, 'c'); + expect(n).to.equal(3); + expect(garr).to.have.lengthOf(3); + expect(garr).to.deep.equal(['a', { foo: 'bar' }, 'c']); + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; }); - describe('push()', () => { - it('should return 0 if not adding any elements', () => { - const garr = new GawkArray; - const n = garr.push(); - expect(n).to.equal(0); - expect(garr).to.have.lengthOf(0); + it('should not clobber pushed object instance', () => { + const emitter = Object.assign(new EventEmitter, { + pid: 666, + command: 'foo', + args: ['bar', 'baz'], + options: {}, + startTime: new Date }); - it('should add a new element', () => { - const garr = new GawkArray; - let n = garr.push('a'); - expect(n).to.equal(1); - expect(garr).to.have.lengthOf(1); - expect(garr).to.deep.equal(['a']); - - n = garr.push('b'); - expect(n).to.equal(2); - expect(garr).to.have.lengthOf(2); - expect(garr).to.deep.equal(['a', 'b']); - }); + const garr = gawk([]); + garr.push(emitter); - it('should add multiple new elements', () => { - const garr = new GawkArray; - let n = garr.push('a', { foo: 'bar' }, 'c'); - expect(n).to.equal(3); - expect(garr).to.have.lengthOf(3); - expect(garr).to.deep.equal(['a', { foo: 'bar' }, 'c']); - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - }); + expect(garr[0]).to.be.instanceOf(EventEmitter); + expect(garr[0].on).to.be.a.Function; }); +}); - describe('shift()', () => { - it('should remove an element from the front of the array', () => { - const garr = gawk(['a', 'b']); - const str = garr.shift(); - expect(str).to.equal('a'); - expect(garr.length).to.equal(1); - expect(garr).to.deep.equal(['b']); - }); +describe('shift()', () => { + it('should remove an element from the front of the array', () => { + const garr = gawk(['a', 'b']); + const str = garr.shift(); + expect(str).to.equal('a'); + expect(garr.length).to.equal(1); + expect(garr).to.deep.equal(['b']); + }); - it('should return undefined when shifting an empty array', () => { - const garr = gawk([]); - const str = garr.shift(); - expect(str).to.be.undefined; - expect(garr.length).to.equal(0); - }); + it('should return undefined when shifting an empty array', () => { + const garr = gawk([]); + const str = garr.shift(); + expect(str).to.be.undefined; + expect(garr.length).to.equal(0); + }); - it('should detach shifted gawk object', () => { - const gobj = gawk({}); - const garr = gawk([gobj]); - expect(gobj.__gawk__.parents.has(garr)).to.be.true; - const gobj2 = garr.shift(); - expect(gobj2).to.equal(gobj); - expect(gobj.__gawk__.parents.has(garr)).to.be.false; - }); + it('should detach shifted gawk object', () => { + const gobj = gawk({}); + const garr = gawk([gobj]); + expect(gobj.__gawk__.parents.has(garr)).to.be.true; + const gobj2 = garr.shift(); + expect(gobj2).to.equal(gobj); + expect(gobj.__gawk__.parents.has(garr)).to.be.false; }); +}); - describe('splice()', () => { - it('should do nothing if no args', () => { - const garr = new GawkArray('a', 'b', 'c'); - const arr = garr.splice(); - expect(arr).to.have.lengthOf(0); - expect(garr).to.deep.equal(['a', 'b', 'c']); - }); +describe('splice()', () => { + it('should do nothing if no args', () => { + const garr = gawk(['a', 'b', 'c']); + const arr = garr.splice(); + expect(arr).to.have.lengthOf(0); + expect(garr).to.deep.equal(['a', 'b', 'c']); + }); - it('should remove elements after the start index', () => { - const garr = new GawkArray('a', 'b', 'c'); - const arr = garr.splice(1); - expect(arr).to.have.lengthOf(2); - expect(arr).to.deep.equal(['b', 'c']); - expect(garr).to.deep.equal(['a']); - }); + it('should remove elements after the start index', () => { + const garr = gawk(['a', 'b', 'c']); + const arr = garr.splice(1); + expect(arr).to.have.lengthOf(2); + expect(arr).to.deep.equal(['b', 'c']); + expect(garr).to.deep.equal(['a']); + }); - it('should detach and remove gawk objects after the start index', () => { - const garr = new GawkArray({ a: 1 }, { b: 2 }, { c: 3 }); - expect(garr[0].__gawk__.parents.has(garr)).to.be.true; - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - - const arr = garr.splice(1); - expect(arr).to.have.lengthOf(2); - expect(arr).to.deep.equal([ { b: 2 }, { c: 3 } ]); - expect(garr).to.deep.equal([ { a: 1 } ]); - expect(garr[0].__gawk__.parents.has(garr)).to.be.true; - expect(arr[0].__gawk__.parents.has(garr)).to.be.false; - expect(arr[0].__gawk__.parents.has(garr)).to.be.false; - }); + it('should detach and remove gawk objects after the start index', () => { + const garr = gawk([ { a: 1 }, { b: 2 }, { c: 3 } ]); + expect(garr[0].__gawk__.parents.has(garr)).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + + const arr = garr.splice(1); + expect(arr).to.have.lengthOf(2); + expect(arr).to.deep.equal([ { b: 2 }, { c: 3 } ]); + expect(garr).to.deep.equal([ { a: 1 } ]); + expect(garr[0].__gawk__.parents.has(garr)).to.be.true; + expect(arr[0].__gawk__.parents.has(garr)).to.be.false; + expect(arr[0].__gawk__.parents.has(garr)).to.be.false; + }); - it('should remove elements and add elements', () => { - const garr = new GawkArray({ a: 1 }, { b: 2 }, { c: 3 }); - expect(garr[0].__gawk__.parents.has(garr)).to.be.true; - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - - const arr = garr.splice(1, 1, { d: 4 }, { e: 5 }); - expect(arr).to.have.lengthOf(1); - expect(arr).to.deep.equal([ { b: 2 } ]); - expect(garr).to.deep.equal([ { a: 1 }, { d: 4 }, { e: 5 }, { c: 3 } ]); - expect(garr[0].__gawk__.parents.has(garr)).to.be.true; - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - expect(garr[2].__gawk__.parents.has(garr)).to.be.true; - expect(garr[3].__gawk__.parents.has(garr)).to.be.true; - expect(arr[0].__gawk__.parents.has(garr)).to.be.false; - }); + it('should remove elements and add elements', () => { + const garr = gawk([ { a: 1 }, { b: 2 }, { c: 3 } ]); + expect(garr[0].__gawk__.parents.has(garr)).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + + const arr = garr.splice(1, 1, { d: 4 }, { e: 5 }); + expect(arr).to.have.lengthOf(1); + expect(arr).to.deep.equal([ { b: 2 } ]); + expect(arr[0].__gawk__.parents.has(garr)).to.be.false; + + expect(garr).to.deep.equal([ { a: 1 }, { d: 4 }, { e: 5 }, { c: 3 } ]); + expect(garr[0].__gawk__.parents.has(garr)).to.be.true; + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; + expect(garr[2].__gawk__.parents.has(garr)).to.be.true; + expect(garr[3].__gawk__.parents.has(garr)).to.be.true; }); +}); - describe('unshift()', () => { - it('should return 0 if not adding any elements', () => { - const garr = new GawkArray; - const n = garr.unshift(); - expect(n).to.equal(0); - expect(garr).to.have.lengthOf(0); - }); +describe('unshift()', () => { + it('should return 0 if not adding any elements', () => { + const garr = gawk([]); + const n = garr.unshift(); + expect(n).to.equal(0); + expect(garr).to.have.lengthOf(0); + }); - it('should add a new element', () => { - const garr = new GawkArray; - let n = garr.unshift('a'); - expect(n).to.equal(1); - expect(garr).to.have.lengthOf(1); - expect(garr).to.deep.equal(['a']); - - n = garr.unshift('b'); - expect(n).to.equal(2); - expect(garr).to.have.lengthOf(2); - expect(garr).to.deep.equal(['b', 'a']); - }); + it('should add a new element', () => { + const garr = gawk([]); + let n = garr.unshift('a'); + expect(n).to.equal(1); + expect(garr).to.have.lengthOf(1); + expect(garr).to.deep.equal(['a']); + + n = garr.unshift('b'); + expect(n).to.equal(2); + expect(garr).to.have.lengthOf(2); + expect(garr).to.deep.equal(['b', 'a']); + }); - it('should add multiple new elements', () => { - const garr = new GawkArray; - let n = garr.unshift('a', { foo: 'bar' }, 'c'); - expect(n).to.equal(3); - expect(garr).to.have.lengthOf(3); - expect(garr).to.deep.equal(['a', { foo: 'bar' }, 'c']); - expect(garr[1].__gawk__.parents.has(garr)).to.be.true; - }); + it('should add multiple new elements', () => { + const garr = gawk([]); + let n = garr.unshift('a', { foo: 'bar' }, 'c'); + expect(n).to.equal(3); + expect(garr).to.have.lengthOf(3); + expect(garr).to.deep.equal(['a', { foo: 'bar' }, 'c']); + expect(garr[1].__gawk__.parents.has(garr)).to.be.true; }); }); diff --git a/test/test-object.js b/test/test-object.js index 6408e61..bd157fd 100644 --- a/test/test-object.js +++ b/test/test-object.js @@ -1,380 +1,388 @@ -import gawk, { GawkArray, GawkObject } from '../src/index'; - -describe('GawkObject', () => { - describe('gawk()', () => { - it('should gawk empty object', () => { - const obj = {}; - const gobj = gawk(obj); - expect(gobj).to.be.an.instanceof(GawkObject); - expect(gobj).to.deep.equal(obj); - }); +import gawk, { Gawk, isGawked } from '../src/index'; - it('should gawk object with mixed values', () => { - const obj = { - foo: 'bar', - num: 123, - pi: 3.14, - undef: undefined, - nul: null, - nan: NaN, - arr: ['a', 123, 4.56, null, undefined], - fn: function () {} - }; - const gobj = gawk(obj); - expect(gobj).to.be.an.instanceof(GawkObject); - expect(Object.keys(gobj)).to.deep.equal(Object.keys(obj)); - expect(gobj).to.be.an.object; - expect(gobj).to.deep.equal(obj); - expect(gobj.arr).to.be.instanceof(GawkArray); - expect(gobj.arr.__gawk__.parents.has(gobj)).to.be.true; - }); +import { EventEmitter } from 'events'; - it('should gawk object with nested objects', () => { - const obj = { foo: { bar: 'baz' } }; - const gobj = gawk(obj); - expect(gobj).to.be.an.instanceof(GawkObject); - expect(gobj).to.be.an.object; - expect(gobj).to.deep.equal(obj); - expect(gobj.foo).to.be.instanceof(GawkObject); - expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; - }); +describe('gawk() object', () => { + it('should gawk empty object', () => { + const obj = {}; + const gobj = gawk(obj); + expect(isGawked(gobj)).to.be.true; + expect(gobj).to.deep.equal(obj); + }); - it('should create a gawk object without an explicit value', () => { - const gobj = new GawkObject; - expect(gobj).to.deep.equal({}); - }); + it('should passhthrough non-object values', () => { + const gobj = gawk('hello!'); + expect(isGawked(gobj)).to.be.false; + expect(gobj).to.equal('hello!'); + }); - it('should fail if parent is not a gawk object', () => { - expect(() => { - gawk({}, 'foo'); - }).to.throw(TypeError, 'Expected parent to be a GawkArray or GawkObject'); - }); + it('should gawk object with mixed values', () => { + const obj = { + foo: 'bar', + num: 123, + pi: 3.14, + undef: undefined, + nul: null, + nan: NaN, + arr: ['a', 123, 4.56, null, undefined], + fn: function () {} + }; + const gobj = gawk(obj); + expect(isGawked(gobj)).to.be.true; + expect(Object.keys(gobj)).to.deep.equal(Object.keys(obj)); + expect(gobj).to.be.an.object; + expect(gobj).to.deep.equal(obj); + expect(isGawked(gobj.arr)).to.be.true; + expect(gobj.arr.__gawk__.parents.has(gobj)).to.be.true; }); - describe('new GawkObject()', () => { - it('should not allow __gawk__ to be set', () => { - const gobj = new GawkObject; - expect(gobj).to.have.property('__gawk__'); - expect(() => { - gobj.__gawk__ = 'foo'; - }).to.throw(Error); - }); + it('should gawk object with nested objects', () => { + const obj = { foo: { bar: 'baz' } }; + const gobj = gawk(obj); + expect(isGawked(gobj)).to.be.true; + expect(gobj).to.be.an.object; + expect(gobj).to.deep.equal(obj); + expect(isGawked(gobj.foo)).to.be.true; + expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; + }); - it('should not allow __gawk__ to be deleted', () => { - const gobj = new GawkObject; - expect(gobj).to.have.property('__gawk__'); - expect(() => { - delete gobj.__gawk__; - }).to.throw(Error); - }); + it('should fail if parent is not a gawk object', () => { + expect(() => { + gawk({}, 'foo'); + }).to.throw(TypeError, 'Expected parent to be gawked'); + }); - it('should fail if gawking an object that is the parent', () => { - const gobj = new GawkObject; - expect(() => { - gawk(gobj, gobj); - }).to.throw(Error); - }); + it('should not clobber Date', () => { + const date = new Date; + const gobj = gawk(date); + expect(isGawked(gobj)).to.be.true; + expect(gobj).to.be.instanceOf(Date); + expect(gobj.getTime).to.be.a.Function; + expect(gobj).to.not.equal(date); + }); - it('should throw TypeError for non-object value', () => { - expect(() => new GawkObject(true)).to.throw(TypeError); - expect(() => new GawkObject('foo')).to.throw(TypeError); - expect(() => new GawkObject(123)).to.throw(TypeError); - expect(() => new GawkObject(3.14)).to.throw(TypeError); - expect(() => new GawkObject(NaN)).to.throw(TypeError); - expect(() => new GawkObject(['a', 'b'])).to.throw(TypeError); - expect(() => new GawkObject(function () {})).to.throw(TypeError); - }); + it('should not clobber EventEmitter', () => { + const emitter = new EventEmitter; + const gobj = gawk(emitter); + expect(isGawked(gobj)).to.be.true; + expect(gobj).to.be.instanceOf(EventEmitter); + expect(gobj.on).to.be.a.Function; + expect(gobj).to.not.equal(emitter); + }); - it('should throw TypeError for non-object gawk type', () => { - expect(() => new GawkObject(gawk(true))).to.throw(TypeError); - expect(() => new GawkObject(gawk('foo'))).to.throw(TypeError); - expect(() => new GawkObject(gawk(123))).to.throw(TypeError); - expect(() => new GawkObject(gawk(3.14))).to.throw(TypeError); - expect(() => new GawkObject(gawk(NaN))).to.throw(TypeError); - expect(() => new GawkObject(gawk(['a', 'b']))).to.throw(TypeError); - expect(() => new GawkObject(gawk(function () {}))).to.throw(TypeError); - }); + it('should not allow __gawk__ to be set', () => { + const gobj = gawk({}); + expect(isGawked(gobj)).to.be.true; + expect(() => { + gobj.__gawk__ = 'foo'; + }).to.throw(Error, 'Cannot override property \'__gawk__\''); + }); - it('should copy another gawked object', () => { - const obj = { foo: 'bar' }; - const gobj = new GawkObject(gawk(obj)); - expect(gobj).to.deep.equal(obj); - }); + it('should not allow __gawk__ to be deleted', () => { + const gobj = gawk({}); + expect(isGawked(gobj)).to.be.true; + expect(() => { + delete gobj.__gawk__; + }).to.throw(Error, 'Cannot delete property \'__gawk__\''); + }); - it('should not clobber date objects', () => { - const dt = new Date; - const gobj = new GawkObject({ dt }); - expect(gobj.dt).to.equal(dt); - }); + it('should fail if gawking an object that is the parent', () => { + const gobj = gawk({}); + expect(isGawked(gobj)).to.be.true; + expect(() => { + gawk(gobj, gobj); + }).to.throw(Error, 'The parent must not be the same object as the value'); }); - describe('toString()', () => { - it('should support toString()', () => { - const obj = { foo: 'bar' }; - const gobj = gawk(obj); - expect(gobj.toString()).to.equal('[object Object]'); - }); + it('should copy another gawked object', () => { + const obj = { foo: 'bar' }; + const gobj = gawk(obj); + expect(isGawked(gobj)).to.be.true; + const gobj2 = gawk(gobj); + expect(isGawked(gobj2)).to.be.true; + expect(gobj).to.deep.equal(obj); + expect(gobj2).to.deep.equal(obj); + expect(gobj2).to.equal(gobj); + }); +}); + +describe('built-ins', () => { + it('should support toString()', () => { + const obj = { foo: 'bar' }; + const gobj = gawk(obj); + expect(gobj.toString()).to.equal('[object Object]'); }); - describe('valueOf()', () => { - it('should support valueOf()', () => { - const obj = { foo: 'bar' }; - const gobj = gawk(obj); - expect(gobj.valueOf()).to.deep.equal(obj); - }); + it('should support valueOf()', () => { + const obj = { foo: 'bar' }; + const gobj = gawk(obj); + expect(gobj.valueOf()).to.deep.equal(obj); }); +}); - describe('get property', () => { - it('should get a value by key', () => { - const str = gawk({ foo: 'bar' }).foo; - expect(str).to.be.an.a.string; - expect(str).to.equal('bar'); - }); +describe('get property', () => { + it('should get a value by key', () => { + const str = gawk({ foo: 'bar' }).foo; + expect(str).to.be.an.a.string; + expect(str).to.equal('bar'); + }); - it('should get undefined for non-existent key', () => { - const undef = gawk({}).foo; - expect(undef).to.be.undefined; - }); + it('should get undefined for non-existent key', () => { + const undef = gawk({}).foo; + expect(undef).to.be.undefined; + }); - it('should get a deeply nested object by key', () => { - const str = gawk({ foo: { bar: 'wiz' } }).foo.bar; - expect(str).to.be.a.string; - expect(str).to.equal('wiz'); - }); + it('should get a deeply nested object by key', () => { + const str = gawk({ foo: { bar: 'wiz' } }).foo.bar; + expect(str).to.be.a.string; + expect(str).to.equal('wiz'); + }); - it('should get a deeply nested non-object by key', () => { - const undef = gawk({ foo: 'wiz' }).foo.bar; - expect(undef).to.be.undefined; - }); + it('should get a deeply nested non-object by key', () => { + const undef = gawk({ foo: 'wiz' }).foo.bar; + expect(undef).to.be.undefined; + }); - it('should get undefined for non-existent deeply nested key', () => { - const undef = gawk({ foo: { } }).foo.bar; - expect(undef).to.be.undefined; - }); + it('should get undefined for non-existent deeply nested key', () => { + const undef = gawk({ foo: { } }).foo.bar; + expect(undef).to.be.undefined; }); +}); - describe('set property', () => { - it('should set a value', () => { - const gobj = gawk({}); - gobj.foo = 'bar'; - expect(gobj).to.deep.equal({ foo: 'bar' }); - }); +describe('set property', () => { + it('should set a value', () => { + const gobj = gawk({}); + gobj.foo = 'bar'; + expect(gobj).to.deep.equal({ foo: 'bar' }); + }); + + it('should create child object and set its value', () => { + const gobj = gawk({}); + gobj.foo = {}; + gobj.foo.bar = 'wiz'; + expect(isGawked(gobj.foo)).to.be.true; + expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; + expect(gobj).to.deep.equal({ foo: { bar: 'wiz' } }); + }); - it('should create child object and set its value', () => { - const gobj = gawk({}); - gobj.foo = {}; - gobj.foo.bar = 'wiz'; - expect(gobj.foo).to.be.instanceof(GawkObject); - expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; - expect(gobj).to.deep.equal({ foo: { bar: 'wiz' } }); + it('should override child value', () => { + const gobj = gawk({ + foo: { + bar: 'baz' + } }); - it('should override child value', () => { - const gobj = gawk({ - foo: { - bar: 'baz' - } - }); + gobj.foo.bar = 'wiz'; - gobj.foo.bar = 'wiz'; + expect(gobj).to.deep.equal({ foo: { bar: 'wiz' } }); + expect(isGawked(gobj.foo)).to.be.true; + expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; + }); - expect(gobj).to.deep.equal({ foo: { bar: 'wiz' } }); - expect(gobj.foo).to.be.instanceof(GawkObject); - expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; + it('should override a different gawk type on set', () => { + const gobj = gawk({ + foo: null }); - it('should override a different gawk type on set', () => { - const gobj = gawk({ - foo: null - }); + gobj.foo = 'bar'; - gobj.foo = 'bar'; + expect(gobj).to.deep.equal({ foo: 'bar' }); + }); +}); - expect(gobj).to.deep.equal({ foo: 'bar' }); - }); +describe('delete property', () => { + it('should delete an existing key/value', () => { + const gobj = gawk({ foo: 'bar' }); + const r = delete gobj.foo; + expect(r).to.be.tru; + expect(Object.keys(gobj).length).to.equal(0); + expect(gobj).to.deep.equal({}); }); - describe('delete property', () => { - it('should delete an existing key/value', () => { - const gobj = gawk({ foo: 'bar' }); - const r = delete gobj.foo; - expect(r).to.be.tru; - expect(Object.keys(gobj).length).to.equal(0); - expect(gobj).to.deep.equal({}); - }); + it('should not error trying to delete non-existent key', () => { + const gobj = gawk({}); + const r = delete gobj.foo; + expect(r).to.be.tru; + expect(Object.keys(gobj).length).to.equal(0); + expect(gobj).to.deep.equal({}); + }); +}); - it('should not error trying to delete non-existent key', () => { - const gobj = gawk({}); - const r = delete gobj.foo; - expect(r).to.be.tru; - expect(Object.keys(gobj).length).to.equal(0); - expect(gobj).to.deep.equal({}); - }); +describe('hasOwnProperty()', () => { + it('should tell if it has a key', () => { + const gobj = gawk({ foo: 'bar' }); + expect(gobj.hasOwnProperty('foo')).to.be.true; + expect(gobj.hasOwnProperty('baz')).to.be.false; }); +}); - describe('hasOwnProperty()', () => { - it('should tell if it has a key', () => { - const gobj = gawk({ foo: 'bar' }); - expect(gobj.hasOwnProperty('foo')).to.be.true; - expect(gobj.hasOwnProperty('baz')).to.be.false; +describe('Object.keys()', () => { + it('should return an array of the keys in the object', () => { + const gobj = gawk({ + foo: 'bar', + pi: 3.14 }); + const keys = Object.keys(gobj); + expect(keys).to.be.an.array; + expect(keys).to.have.lengthOf(2); + expect(keys).to.deep.equal(['foo', 'pi']); }); +}); - describe('Object.keys()', () => { - it('should return an array of the keys in the object', () => { - const gobj = gawk({ - foo: 'bar', - pi: 3.14 - }); - const keys = Object.keys(gobj); - expect(keys).to.be.an.array; - expect(keys).to.have.lengthOf(2); - expect(keys).to.deep.equal(['foo', 'pi']); - }); +describe('JSON.stringify()', () => { + it('should stringify a gawked object', () => { + expect(JSON.stringify(gawk({ foo: 'bar' }))).to.equal('{"foo":"bar"}'); + expect(JSON.stringify(gawk({ foo: 'bar' }), null, ' ')).to.equal('{\n "foo": "bar"\n}'); }); +}); - describe('JSON.stringify()', () => { - it('should stringify a GawkObject', () => { - expect(JSON.stringify(gawk({ foo: 'bar' }))).to.equal('{"foo":"bar"}'); - expect(JSON.stringify(gawk({ foo: 'bar' }), null, ' ')).to.equal('{\n "foo": "bar"\n}'); - }); +describe('merge()', () => { + it('should merge a JS object', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.merge(gobj, { pi: 3.14 }); + expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); }); - describe('merge()', () => { - it('should merge a JS object', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.merge(gobj, { pi: 3.14 }); - expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); - }); + it('should merge a gawked object', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.merge(gobj, gawk({ pi: 3.14 })); + expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); + }); - it('should merge a GawkObject', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.merge(gobj, gawk({ pi: 3.14 })); - expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); - }); + it('should do nothing if not merging anything', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.merge(gobj); + expect(gobj).to.deep.equal({ foo: 'bar' }); + }); - it('should do nothing if not merging anything', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.merge(gobj); - expect(gobj).to.deep.equal({ foo: 'bar' }); - }); + it('should merge multiple JS objects and gawked objects', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.merge(gobj, { baz: 'wiz' }, gawk({ pi: 3.14 }), { num: 123 }, gawk({ arr: ['a', 'b'] })); + expect(gobj).to.deep.equal({ foo: 'bar', baz: 'wiz', pi: 3.14, num: 123, arr: ['a', 'b'] }); + expect(isGawked(gobj.arr)).to.be.true; + expect(gobj.arr.__gawk__.parents.has(gobj)).to.be.true; + }); - it('should merge multiple JS objects and GawkObjects', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.merge(gobj, { baz: 'wiz' }, gawk({ pi: 3.14 }), { num: 123 }, gawk({ arr: ['a', 'b'] })); - expect(gobj).to.deep.equal({ foo: 'bar', baz: 'wiz', pi: 3.14, num: 123, arr: ['a', 'b'] }); - expect(gobj.arr).to.be.instanceof(GawkArray); - expect(gobj.arr.__gawk__.parents.has(gobj)).to.be.true; - }); + it('should merge and overwrite', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.merge(gobj, { foo: 'wiz' }); + expect(gobj).to.deep.equal({ foo: 'wiz' }); + }); - it('should merge and overwrite', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.merge(gobj, { foo: 'wiz' }); - expect(gobj).to.deep.equal({ foo: 'wiz' }); - }); + it('should throw TypeError if no args', () => { + expect(() => { + gawk.merge(); + }).to.throw(TypeError, 'Expected destination to be a gawked object'); + }); - it('should throw TypeError if no args', () => { - expect(() => { gawk.merge(); }).to.throw(TypeError); - }); + it('should fail to merge non-object and non-gawked objects', () => { + const gobj = gawk({}); + expect(() => { gawk.merge(gobj, undefined); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, null); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, true); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, 'foo'); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, 123); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, 3.14); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, NaN); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, ['a', 'b']); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, function () {}); }).to.throw(TypeError, 'Expected merge source to be an object'); + }); - it('should fail to merge non-object and non-GawkObjects types', () => { - const gobj = gawk({}); - expect(() => { gawk.merge(gobj, undefined); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, null); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, true); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, 'foo'); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, 123); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, 3.14); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, NaN); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, ['a', 'b']); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, function () {}); }).to.throw(TypeError); - }); + it('should throw TypeError when setting non-object gawk type', () => { + const gobj = gawk({}); + expect(() => { gawk.merge(gobj, gawk()); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(null)); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(true)); }).to.throw(TypeError), 'Expected merge source to be an object'; + expect(() => { gawk.merge(gobj, gawk('foo')); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(123)); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(3.14)); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(NaN)); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(['a', 'b'])); }).to.throw(TypeError, 'Expected merge source to be an object'); + expect(() => { gawk.merge(gobj, gawk(function () {})); }).to.throw(TypeError, 'Expected merge source to be an object'); + }); - it('should throw TypeError when setting non-object gawk type', () => { - const gobj = gawk({}); - expect(() => { gawk.merge(gobj, gawk()); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(null)); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(true)); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk('foo')); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(123)); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(3.14)); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(NaN)); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(['a', 'b'])); }).to.throw(TypeError); - expect(() => { gawk.merge(gobj, gawk(function () {})); }).to.throw(TypeError); - }); + it('should assign another gawk object', () => { + const gobj = gawk({ foo: 'bar' }); + const gobj2 = gawk({ pi: 3.14 }); + gawk.merge(gobj, gobj2); + expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); + }); - it('should assign another gawk object', () => { - const gobj = gawk({ foo: 'bar' }); - const gobj2 = gawk({ pi: 3.14 }); - gawk.merge(gobj, gobj2); - expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); - }); + it('should shallow merge', () => { + const gobj = gawk({ foo: { bar: { baz: 'wiz' } } }); + gawk.merge(gobj, { foo: { pi: 3.14 } }); + expect(gobj).to.deep.equal({ foo: { pi: 3.14 }}); + expect(isGawked(gobj.foo)).to.be.true; + expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; + }); +}); - it('should shallow merge', () => { - const gobj = gawk({ foo: { bar: { baz: 'wiz' } } }); - gawk.merge(gobj, { foo: { pi: 3.14 } }); - expect(gobj).to.deep.equal({ foo: { pi: 3.14 }}); - expect(gobj.foo).to.be.instanceof(GawkObject); - expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; - }); +describe('mergeDeep()', () => { + it('should merge a JS object', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.mergeDeep(gobj, { pi: 3.14 }); + expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); }); - describe('mergeDeep()', () => { - it('should merge a JS object', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.mergeDeep(gobj, { pi: 3.14 }); - expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); - }); + it('should merge a gawked object', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.mergeDeep(gobj, gawk({ pi: 3.14 })); + expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); + }); - it('should merge a GawkObject', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.mergeDeep(gobj, gawk({ pi: 3.14 })); - expect(gobj).to.deep.equal({ foo: 'bar', pi: 3.14 }); - }); + it('should merge multiple JS objects and gawked objects', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.mergeDeep(gobj, { baz: 'wiz' }, gawk({ pi: 3.14 }), { num: 123 }, gawk({ arr: ['a', 'b'] })); + expect(gobj).to.deep.equal({ foo: 'bar', baz: 'wiz', pi: 3.14, num: 123, arr: ['a', 'b'] }); + expect(isGawked(gobj.arr)).to.be.true; + expect(gobj.arr.__gawk__.parents.has(gobj)).to.be.true; + }); - it('should merge multiple JS objects and GawkObjects', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.mergeDeep(gobj, { baz: 'wiz' }, gawk({ pi: 3.14 }), { num: 123 }, gawk({ arr: ['a', 'b'] })); - expect(gobj).to.deep.equal({ foo: 'bar', baz: 'wiz', pi: 3.14, num: 123, arr: ['a', 'b'] }); - expect(gobj.arr).to.be.instanceof(GawkArray); - expect(gobj.arr.__gawk__.parents.has(gobj)).to.be.true; - }); + it('should merge and overwrite', () => { + let gobj = gawk({ foo: 'bar' }); + gobj = gawk.mergeDeep(gobj, { foo: 'wiz' }); + expect(gobj).to.deep.equal({ foo: 'wiz' }); + }); - it('should merge and overwrite', () => { - let gobj = gawk({ foo: 'bar' }); - gobj = gawk.mergeDeep(gobj, { foo: 'wiz' }); - expect(gobj).to.deep.equal({ foo: 'wiz' }); - }); + it('should throw TypeError if no args', () => { + expect(() => { + gawk.mergeDeep(); + }).to.throw(TypeError, 'Expected destination to be a gawked object'); + }); - it('should throw TypeError if no args', () => { - expect(() => { gawk.mergeDeep(); }).to.throw(TypeError); - }); + it('should fail to merge non-object and non-gawked objects types', () => { + expect(() => { + gawk.mergeDeep('foo'); + }).to.throw(TypeError, 'Expected destination to be a gawked object'); - it('should fail to merge non-object and non-GawkObjects types', () => { - expect(() => { gawk.mergeDeep('foo'); }).to.throw(TypeError); - expect(() => { gawk.mergeDeep({}, gawk('foo')); }).to.throw(TypeError); - }); + expect(() => { + gawk.mergeDeep({}, gawk('foo')); + }).to.throw(TypeError, 'Expected merge source to be an object'); + }); - it('should deep merge', () => { - let gobj = gawk({ foo: { bar: { baz: 'wiz' } } }); - gobj = gawk.mergeDeep(gobj, { foo: { pi: 3.14, biz: { wap: 'fip' } } }); - expect(gobj).to.deep.equal({ - foo: { - bar: { - baz: 'wiz' - }, - pi: 3.14, - biz: { - wap: 'fip' - } + it('should deep merge', () => { + let gobj = gawk({ foo: { bar: { baz: 'wiz' } } }); + gobj = gawk.mergeDeep(gobj, { foo: { pi: 3.14, biz: { wap: 'fip' } } }); + + expect(gobj).to.deep.equal({ + foo: { + bar: { + baz: 'wiz' + }, + pi: 3.14, + biz: { + wap: 'fip' } - }); - expect(gobj.foo).to.be.instanceof(GawkObject); - expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; - expect(gobj.foo.bar).to.be.instanceof(GawkObject); - expect(gobj.foo.bar.__gawk__.parents.has(gobj.foo)).to.be.true; - expect(gobj.foo.biz).to.be.instanceof(GawkObject); - expect(gobj.foo.biz.__gawk__.parents.has(gobj.foo)).to.be.true; + } }); + + expect(isGawked(gobj.foo)).to.be.true; + expect(gobj.foo.__gawk__.parents.has(gobj)).to.be.true; + + expect(isGawked(gobj.foo.bar)).to.be.true; + expect(gobj.foo.bar.__gawk__.parents.has(gobj.foo)).to.be.true; + + expect(isGawked(gobj.foo.biz)).to.be.true; + expect(gobj.foo.biz.__gawk__.parents.has(gobj.foo)).to.be.true; }); }); diff --git a/test/test-watch.js b/test/test-watch.js index 7ed34f0..1497e10 100644 --- a/test/test-watch.js +++ b/test/test-watch.js @@ -1,95 +1,102 @@ -import gawk, { GawkArray, GawkObject } from '../src/index'; +import gawk, { isGawked } from '../src/index'; describe('gawk.watch()', () => { it('should fail to watch with non-gawk or object type', () => { expect(() => { gawk.watch('foo'); - }).to.throw(TypeError, 'Expected source to be a GawkArray or GawkObject'); + }).to.throw(TypeError, 'Expected subject to be gawked'); }); it('should fail to watch with invalid filter', () => { expect(() => { - gawk.watch(new GawkObject, 123); - }).to.throw(TypeError, 'Expected filter to be a stirng or array of strings'); + gawk.watch(gawk({}), 123); + }).to.throw(TypeError, 'Expected filter to be a string or array of strings'); }); it('should fail to watch with non-function listener', () => { expect(() => { - gawk.watch(new GawkObject); + gawk.watch(gawk({})); }).to.throw(TypeError, 'Expected listener to be a function'); expect(() => { - gawk.watch(new GawkObject, 'foo', 'bar'); + gawk.watch(gawk({}), 'foo', 'bar'); }).to.throw(TypeError, 'Expected listener to be a function'); }); it('should be notified after merge', () => { const gobj = gawk({ foo: 'bar' }); - gawk.watch(gobj, obj => { + const callback = sinon.spy(obj => { expect(obj).to.equal(gobj); expect(obj).to.deep.equal({ foo: 'bar', bar: 'wiz' }); }); + gawk.watch(gobj, callback); gawk.merge(gobj, { bar: 'wiz' }); + expect(callback).to.be.calledOnce; }); it('should be notified when a key/value is added', () => { const gobj = gawk({ foo: 'bar' }); - gawk.watch(gobj, obj => { + const callback = sinon.spy(obj => { expect(obj).to.equal(gobj); expect(obj).to.deep.equal({ foo: 'bar', pi: 3.14 }); }); + gawk.watch(gobj, callback); gobj.pi = 3.14; + expect(callback).to.be.calledOnce; }); it('should be notified when a key/value changes', () => { const gobj = gawk({ foo: 'bar' }); - gawk.watch(gobj, obj => { + const callback = sinon.spy(obj => { expect(obj).to.equal(gobj); expect(obj).to.deep.equal({ foo: 'wiz' }); }); + gawk.watch(gobj, callback); gobj.foo = 'wiz'; + expect(callback).to.be.calledOnce; }); it('should be notified when a key/value is deleted', () => { const gobj = gawk({ foo: 'bar', pi: 3.14 }); - gawk.watch(gobj, obj => { + const callback = sinon.spy(obj => { expect(obj).to.equal(gobj); expect(obj).to.deep.equal({ foo: 'bar' }); }); + gawk.watch(gobj, callback); delete gobj.pi; + expect(callback).to.be.calledOnce; }); it('should not notify when a non-existent key/value is deleted', () => { const gobj = gawk({}); - gawk.watch(gobj, obj => { - throw new Error('Listener should not have been invoked'); - }); + const callback = sinon.spy(); + gawk.watch(gobj, callback); delete gobj.foo; + expect(callback).to.be.notCalled; }); it('should only notify if key/value is uniquely changed', () => { const gobj = gawk({ foo: 'bar' }); - let count = 0; + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); + expect(callback).to.be.notCalled; gobj.foo = 'baz'; - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; gobj.foo = 'baz'; - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should be notified when child changes', () => { const gobj = gawk({ foo: { bar: 'baz' } }); const nested = gobj.foo; - expect(nested).to.be.instanceof(GawkObject); + expect(isGawked(nested)).to.be.true; expect(nested.__gawk__.parents.has(gobj)).to.be.true; - gawk.watch(gobj, (obj, source) => { + const callback = sinon.spy((obj, source) => { expect(obj).to.equal(gobj); expect(obj).to.deep.equal({ foo: { @@ -103,63 +110,59 @@ describe('gawk.watch()', () => { }); }); + gawk.watch(gobj, callback); + nested.pi = 3.14; + + expect(callback).to.be.calledOnce; }); it('should notify child watchers if child changes', () => { const gobj = gawk({ foo: { bar: 'baz' } }); - let count = 0; + const callback = sinon.spy(); - gawk.watch(gobj.foo, (obj, source) => { - count++; - }); + gawk.watch(gobj.foo, callback); gawk.mergeDeep(gobj, { foo: { bar: 'baz' + Date.now() } }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should be notified when array changes by merge', () => { - let count = 0; const gobj = gawk({ foo: [] }); + const callback = sinon.spy(); - gawk.watch(gobj.foo, obj => { - count++; - }); + gawk.watch(gobj.foo, callback); gawk.mergeDeep(gobj, { foo: [1, 2, 3] }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; expect(gobj).to.deep.equal({ foo: [1, 2, 3] }); gawk.mergeDeep(gobj, { foo: [4, 5, 6] }); - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; expect(gobj).to.deep.equal({ foo: [4, 5, 6] }); }); it('should be notified when array changes by deep merge', () => { - let count = 0; const gobj = gawk({ foo: { bar: [] } }); + const callback = sinon.spy(); - gawk.watch(gobj.foo, obj => { - count++; - }); + gawk.watch(gobj.foo, callback); gawk.mergeDeep(gobj, { foo: { bar: [1, 2, 3] } }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; expect(gobj).to.deep.equal({ foo: { bar: [1, 2, 3] } }); gawk.mergeDeep(gobj, { foo: { bar: [4, 5, 6] } }); - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; expect(gobj).to.deep.equal({ foo: { bar: [4, 5, 6] } }); }); it('should only notify once after deep merge', () => { - const gobj = new GawkObject; - let counter = 0; + const gobj = gawk({}); + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - counter++; - }); + gawk.watch(gobj, callback); gawk.mergeDeep(gobj, { foo: { @@ -213,7 +216,7 @@ describe('gawk.watch()', () => { } }); - expect(counter).to.equal(2); + expect(callback).to.be.calledTwice; }); it('should be notified when deep child changes', () => { @@ -223,21 +226,19 @@ describe('gawk.watch()', () => { gobj.foo.bar.baz = []; const arr = gobj.foo.bar.baz; - let count = 0; - gawk.watch(gobj, obj => { - count++; - }); + const callback = sinon.spy(); + gawk.watch(gobj, callback); arr.push('a'); arr.push('b'); - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; }); it('should notify multiple parents', () => { const gobjs = [ - new GawkObject, - new GawkObject, - new GawkObject + gawk({}), + gawk({}), + gawk({}) ]; const child = gawk({}); child.bar = []; @@ -261,50 +262,46 @@ describe('gawk.watch()', () => { it('should pause and resume notifications', () => { const gobj = gawk({ foo: 'bar' }); - let counter = 0; + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - counter++; - }); + gawk.watch(gobj, callback); gobj.pi = 3.14; - expect(counter).to.equal(1); + expect(callback).to.be.calledOnce; - const gobj2 = new GawkObject({ wiz: 123 }); + const gobj2 = gawk({ wiz: 123 }); gobj.__gawk__.pause(); gobj.baz = gobj2; - expect(counter).to.equal(1); + expect(callback).to.be.calledOnce; gobj.__gawk__.resume(); - expect(counter).to.equal(2); + expect(callback).to.be.calledTwice; gobj.__gawk__.pause(); gobj.color = 'red'; - expect(counter).to.equal(2); + expect(callback).to.be.calledTwice; gobj2.lorum = 'ipsum'; - expect(counter).to.equal(2); + expect(callback).to.be.calledTwice; gobj.__gawk__.resume(); - expect(counter).to.equal(3); + expect(callback).to.be.calledThrice; }); it('should notify parent when child has a merge', () => { const gobj = gawk({}); - let count = 0; + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); const child = gawk({}); gobj.foo = child; // { foo: {} } - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; expect(child.__gawk__.parents.has(gobj)).to.be.true; const bar = gawk({ bar: 'wiz' }); gawk.merge(child, bar); // { foo: { bar: 'wiz' } } - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; expect(bar.__gawk__.parents.has(child)).to.be.false; const foo = gobj.foo; @@ -312,7 +309,7 @@ describe('gawk.watch()', () => { gawk.merge(child, { bar: 'wow' }); // { foo: { bar: 'wow' } } - expect(count).to.equal(3); + expect(callback).to.be.calledThrice; expect(bar.__gawk__.parents.has(child)).to.be.false; expect(child.__gawk__.parents.has(gobj)).to.be.true; @@ -321,41 +318,37 @@ describe('gawk.watch()', () => { it('should only notify parent one time when merging multiple objects', () => { const gobj = gawk({ foo: 'bar' }); - let count = 0; + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); gawk.merge(gobj, { baz: 'wiz' }, gawk({ pi: 3.14 })); //, { num: 123 }, gawk({ arr: ['a', 'b'] })); expect(gobj).to.deep.equal({ foo: 'bar', baz: 'wiz', pi: 3.14 }); //, num: 123, arr: ['a', 'b'] }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should notify parent one time when child has a deep merge', () => { const gobj = gawk({}); - let count = 0; + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); const child = gawk({ bar: { pow: 123 } }); gobj.foo = child; // { foo: { bar: { pow: 123 } } } - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; const bar = gawk({ bar: { wiz: 456 } }); gawk.mergeDeep(child, bar); // { foo: { bar: { pow: 123, wiz: 456 } } } - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; const foo = gobj.foo; expect(foo.__gawk__.parents.has(gobj)).to.be.true; gawk.mergeDeep(child, { bar: { paz: 789 } }); // { foo: { bar: { pow: 123, wiz: 456, paz: 789 } } } - expect(count).to.equal(3); + expect(callback).to.be.calledThrice; expect(bar.__gawk__.parents.has(child)).to.be.false; expect(gobj).to.deep.equal({ foo: { bar: { pow: 123, wiz: 456, paz: 789 } } }); @@ -363,55 +356,49 @@ describe('gawk.watch()', () => { it('should only notify parent one time when merging multiple objects', () => { const gobj = gawk({ foo: { bar: { baz: 'wiz' } } }); - let count = 0; + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); gawk.mergeDeep(gobj, { foo: { bar: { baz: 'wow' } } }); expect(gobj).to.deep.equal({ foo: { bar: { baz: 'wow' } } }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should only notify if value is uniquely changed', () => { const garr = gawk(['a']); - let count = 0; + const callback = sinon.spy(); - gawk.watch(garr, obj => { - count++; - }); + gawk.watch(garr, callback); garr.push('b'); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; garr[1] = 'b'; - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; garr[1] = 'c'; - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; }); it('should be notified when array changes', () => { - let count = 0; const gobj = gawk(['a', 'b']); + const callback = sinon.spy(); - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); gobj.push('c'); gobj.pop(); gobj.unshift('d'); gobj.shift(); - expect(count).to.equal(4); + expect(callback).to.have.callCount(4); }); it('should be notified when deeply nested children change', () => { - let count1 = 0; - let count2 = 0; - let count3 = 0; + const callback1 = sinon.spy(); + const callback2 = sinon.spy(); + const callback3 = sinon.spy(); const arr1 = gawk([]); const arr2 = gawk([]); @@ -423,23 +410,15 @@ describe('gawk.watch()', () => { expect(arr2.__gawk__.parents.has(arr1)).to.be.true; expect(arr3.__gawk__.parents.has(arr2)).to.be.true; - gawk.watch(arr1, obj => { - count1++; - }); - - gawk.watch(arr2, obj => { - count2++; - }); - - gawk.watch(arr3, obj => { - count3++; - }); + gawk.watch(arr1, callback1); + gawk.watch(arr2, callback2); + gawk.watch(arr3, callback3); arr3.push('foo'); - expect(count1).to.equal(1); - expect(count2).to.equal(1); - expect(count3).to.equal(1); + expect(callback1).to.be.calledOnce; + expect(callback2).to.be.calledOnce; + expect(callback3).to.be.calledOnce; expect(arr3.length).to.equal(1); expect(arr3).to.deep.equal(['foo']); @@ -453,52 +432,46 @@ describe('gawk.watch()', () => { it('should notify parent if property is deleted', () => { const gobj = gawk({ foo: { bar: 'baz' } }); + const callback = sinon.spy(); - let count = 0; - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); expect(gobj).to.deep.equal({ foo: { bar: 'baz' } }); delete gobj.foo; expect(gobj).to.deep.equal({}); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should notify parent if index is deleted', () => { const gobj = gawk({ foo: { bar: [ 'baz' ] } }); + const callback = sinon.spy(); - let count = 0; - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); expect(gobj).to.deep.equal({ foo: { bar: [ 'baz' ] } }); delete gobj.foo.bar[0]; expect(gobj).to.deep.equal({ foo: { bar: [] } }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should copy listeners from another gawk object', () => { const gobj = gawk({ foo: 'bar' }); + const callback = sinon.spy(); - let count = 0; - gawk.watch(gobj, obj => { - count++; - }); + gawk.watch(gobj, callback); - const gobj2 = new GawkObject(gobj); + const gobj2 = gawk(gobj); expect(gobj2).to.deep.equal({ foo: 'bar' }); gobj2.baz = 'wiz'; expect(gobj2).to.deep.equal({ foo: 'bar', baz: 'wiz' }); - expect(count).to.equal(1); + expect(callback).to.be.calledOnce; }); it('should watch non-object/array properties of a gawk object', () => { - const gobj = new GawkObject({ foo: { bar: [1, 2, 3] } }); + const gobj = gawk({ foo: { bar: [1, 2, 3] } }); let count = 0; gawk.watch(gobj, ['foo', 'baz'], obj => { @@ -538,11 +511,11 @@ describe('gawk.unwatch()', () => { it('should fail to unwatch with non-gawk or object type', () => { expect(() => { gawk.unwatch('foo'); - }).to.throw(TypeError, 'Expected source to be a GawkArray or GawkObject'); + }).to.throw(TypeError, 'Expected subject to be gawked'); expect(() => { gawk.unwatch({}); - }).to.throw(TypeError, 'Expected source to be a GawkArray or GawkObject'); + }).to.throw(TypeError, 'Expected subject to be gawked'); }); it('should fail to unwatch with non-function listener', () => { @@ -551,24 +524,21 @@ describe('gawk.unwatch()', () => { }).to.throw(TypeError, 'Expected listener to be a function'); }); - it('should unwatch GawkObject changes', () => { + it('should unwatch gawked object changes', () => { const gobj = gawk({}); - let count = 0; - const listener = () => { - count++; - }; + const callback = sinon.spy(); - gawk.watch(gobj, listener); + gawk.watch(gobj, callback); gobj.a = 'b'; gobj.c = 'd'; - gawk.unwatch(gobj, listener); + gawk.unwatch(gobj, callback); gobj.e = 'f'; gobj.g = 'h'; - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; }); it('should unwatch all listeners', () => { @@ -594,23 +564,20 @@ describe('gawk.unwatch()', () => { expect(count).to.equal(4); }); - it('should unwatch GawkArray changes', () => { + it('should unwatch gawked array changes', () => { const garr = gawk(['a']); - let count = 0; - const listener = obj => { - count++; - }; + const callback = sinon.spy(); - gawk.watch(garr, listener); + gawk.watch(garr, callback); garr.unshift('b'); garr.unshift('c'); - gawk.unwatch(garr, listener); + gawk.unwatch(garr, callback); garr.unshift('d'); garr.unshift('e'); - expect(count).to.equal(2); + expect(callback).to.be.calledTwice; }); }); diff --git a/yarn.lock b/yarn.lock index 2ab9d04..0429906 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@gulp-sourcemaps/map-sources@1.X": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" + dependencies: + normalize-path "^2.0.1" + through2 "^2.0.3" + abab@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" @@ -22,10 +29,6 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" -acorn@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - acorn@4.X: version "4.0.11" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" @@ -38,6 +41,10 @@ acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" +acorn@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" + ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" @@ -136,8 +143,8 @@ async@1.x, async@^1.4.0, async@~1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" async@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.2.0.tgz#c324eba010a237e4fbd55a12dee86367d5c0ef32" + version "2.3.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.3.0.tgz#1013d1051047dd320fe24e494d5c66ecaf6147d9" dependencies: lodash "^4.14.0" @@ -165,19 +172,19 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0, babel-code-frame@^6.8.0: esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@^6.0.2, babel-core@^6.21.0, babel-core@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.0.tgz#8f36a0a77f5c155aed6f920b844d23ba56742a02" +babel-core@^6.0.2, babel-core@^6.21.0, babel-core@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" dependencies: babel-code-frame "^6.22.0" - babel-generator "^6.24.0" - babel-helpers "^6.23.0" + babel-generator "^6.24.1" + babel-helpers "^6.24.1" babel-messages "^6.23.0" - babel-register "^6.24.0" + babel-register "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.1" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" babylon "^6.11.0" convert-source-map "^1.1.0" debug "^2.1.1" @@ -209,93 +216,93 @@ babel-generator@6.11.4: lodash "^4.2.0" source-map "^0.5.0" -babel-generator@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.0.tgz#eba270a8cc4ce6e09a61be43465d7c62c1f87c56" +babel-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" dependencies: babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" detect-indent "^4.0.0" jsesc "^1.3.0" lodash "^4.2.0" source-map "^0.5.0" trim-right "^1.0.1" -babel-helper-call-delegate@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" dependencies: - babel-helper-hoist-variables "^6.22.0" + babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" - babel-traverse "^6.22.0" - babel-types "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-define-map@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.23.0.tgz#1444f960c9691d69a2ced6a205315f8fd00804e7" +babel-helper-define-map@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" dependencies: - babel-helper-function-name "^6.23.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" lodash "^4.2.0" -babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz#25742d67175c8903dbe4b6cb9d9e1fcb8dcf23a6" +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" dependencies: - babel-helper-get-function-arity "^6.22.0" + babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-get-function-arity@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-hoist-variables@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-optimise-call-expression@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.23.0.tgz#f3ee7eed355b4282138b33d02b78369e470622f5" +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" dependencies: babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" -babel-helper-regex@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz#79f532be1647b1f0ee3474b5f5c3da58001d247d" +babel-helper-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" lodash "^4.2.0" -babel-helper-replace-supers@^6.22.0, babel-helper-replace-supers@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.23.0.tgz#eeaf8ad9b58ec4337ca94223bacdca1f8d9b4bfd" +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" dependencies: - babel-helper-optimise-call-expression "^6.23.0" + babel-helper-optimise-call-expression "^6.24.1" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helpers@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992" +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" dependencies: babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" babel-istanbul@^0.12.1: version "0.12.2" @@ -330,6 +337,19 @@ babel-plugin-check-es2015-constants@^6.22.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -342,36 +362,36 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-block-scoping@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.23.0.tgz#e48895cf0b375be148cd7c8879b422707a053b51" +babel-plugin-transform-es2015-block-scoping@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" dependencies: babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" lodash "^4.2.0" -babel-plugin-transform-es2015-classes@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.23.0.tgz#49b53f326202a2fd1b3bbaa5e2edd8a4f78643c1" +babel-plugin-transform-es2015-classes@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" dependencies: - babel-helper-define-map "^6.23.0" - babel-helper-function-name "^6.23.0" - babel-helper-optimise-call-expression "^6.23.0" - babel-helper-replace-supers "^6.23.0" + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz#7c383e9629bba4820c11b0425bdd6290f7f057e7" +babel-plugin-transform-es2015-computed-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" dependencies: babel-runtime "^6.22.0" - babel-template "^6.22.0" + babel-template "^6.24.1" babel-plugin-transform-es2015-destructuring@^6.22.0: version "6.23.0" @@ -379,12 +399,12 @@ babel-plugin-transform-es2015-destructuring@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz#672397031c21610d72dd2bbb0ba9fb6277e1c36b" +babel-plugin-transform-es2015-duplicate-keys@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-for-of@^6.22.0: version "6.23.0" @@ -392,13 +412,13 @@ babel-plugin-transform-es2015-for-of@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz#f5fcc8b09093f9a23c76ac3d9e392c3ec4b77104" +babel-plugin-transform-es2015-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" dependencies: - babel-helper-function-name "^6.22.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-literals@^6.22.0: version "6.22.0" @@ -406,63 +426,63 @@ babel-plugin-transform-es2015-literals@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-modules-amd@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.0.tgz#a1911fb9b7ec7e05a43a63c5995007557bcf6a2e" +babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.0" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.22.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-commonjs@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.0.tgz#e921aefb72c2cc26cb03d107626156413222134f" +babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" dependencies: - babel-plugin-transform-strict-mode "^6.22.0" + babel-plugin-transform-strict-mode "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-modules-systemjs@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.23.0.tgz#ae3469227ffac39b0310d90fec73bfdc4f6317b0" +babel-plugin-transform-es2015-modules-systemjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" dependencies: - babel-helper-hoist-variables "^6.22.0" + babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-umd@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.0.tgz#fd5fa63521cae8d273927c3958afd7c067733450" +babel-plugin-transform-es2015-modules-umd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz#daa60e114a042ea769dd53fe528fc82311eb98fc" +babel-plugin-transform-es2015-object-super@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" dependencies: - babel-helper-replace-supers "^6.22.0" + babel-helper-replace-supers "^6.24.1" babel-runtime "^6.22.0" -babel-plugin-transform-es2015-parameters@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz#3a2aabb70c8af945d5ce386f1a4250625a83ae3b" +babel-plugin-transform-es2015-parameters@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" dependencies: - babel-helper-call-delegate "^6.22.0" - babel-helper-get-function-arity "^6.22.0" + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz#8ba776e0affaa60bff21e921403b8a652a2ff723" +babel-plugin-transform-es2015-shorthand-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-spread@^6.22.0: version "6.22.0" @@ -470,13 +490,13 @@ babel-plugin-transform-es2015-spread@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz#ab316829e866ee3f4b9eb96939757d19a5bc4593" +babel-plugin-transform-es2015-sticky-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" dependencies: - babel-helper-regex "^6.22.0" + babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-template-literals@^6.22.0: version "6.22.0" @@ -490,61 +510,61 @@ babel-plugin-transform-es2015-typeof-symbol@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz#8d9cc27e7ee1decfe65454fb986452a04a613d20" +babel-plugin-transform-es2015-unicode-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" dependencies: - babel-helper-regex "^6.22.0" + babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" regexpu-core "^2.0.0" -babel-plugin-transform-regenerator@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz#65740593a319c44522157538d690b84094617ea6" +babel-plugin-transform-regenerator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" dependencies: - regenerator-transform "0.9.8" + regenerator-transform "0.9.11" -babel-plugin-transform-strict-mode@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c" +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-preset-es2015@^6.18.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.0.tgz#c162d68b1932696e036cd3110dc1ccd303d2673a" + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" dependencies: babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-transform-es2015-arrow-functions "^6.22.0" babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.22.0" - babel-plugin-transform-es2015-classes "^6.22.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.24.1" + babel-plugin-transform-es2015-classes "^6.24.1" + babel-plugin-transform-es2015-computed-properties "^6.24.1" babel-plugin-transform-es2015-destructuring "^6.22.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-duplicate-keys "^6.24.1" babel-plugin-transform-es2015-for-of "^6.22.0" - babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-function-name "^6.24.1" babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.24.0" - babel-plugin-transform-es2015-modules-commonjs "^6.24.0" - babel-plugin-transform-es2015-modules-systemjs "^6.22.0" - babel-plugin-transform-es2015-modules-umd "^6.24.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.22.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-plugin-transform-es2015-modules-systemjs "^6.24.1" + babel-plugin-transform-es2015-modules-umd "^6.24.1" + babel-plugin-transform-es2015-object-super "^6.24.1" + babel-plugin-transform-es2015-parameters "^6.24.1" + babel-plugin-transform-es2015-shorthand-properties "^6.24.1" babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.24.1" babel-plugin-transform-es2015-template-literals "^6.22.0" babel-plugin-transform-es2015-typeof-symbol "^6.22.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" + babel-plugin-transform-es2015-unicode-regex "^6.24.1" + babel-plugin-transform-regenerator "^6.24.1" -babel-register@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.0.tgz#5e89f8463ba9970356d02eb07dabe3308b080cfd" +babel-register@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f" dependencies: - babel-core "^6.24.0" + babel-core "^6.24.1" babel-runtime "^6.22.0" core-js "^2.4.0" home-or-tmp "^2.0.0" @@ -559,13 +579,13 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.9.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.22.0, babel-template@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" +babel-template@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" dependencies: babel-runtime "^6.22.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" babylon "^6.11.0" lodash "^4.2.0" @@ -583,23 +603,23 @@ babel-traverse@6.12.0: invariant "^2.2.0" lodash "^4.2.0" -babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: - version "6.23.1" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" +babel-traverse@^6.23.1, babel-traverse@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" babylon "^6.15.0" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.10.2, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0, babel-types@^6.9.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" +babel-types@^6.10.2, babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.9.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" @@ -649,8 +669,8 @@ boom@2.x.x: hoek "2.x.x" brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + version "1.1.7" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" dependencies: balanced-match "^0.4.1" concat-map "0.0.1" @@ -667,7 +687,7 @@ browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" -buffer-shims@^1.0.0: +buffer-shims@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" @@ -833,8 +853,8 @@ concat-stream@^1.5.2: typedarray "^0.0.6" convert-source-map@1.X, convert-source-map@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.4.0.tgz#e3dad195bf61bfe13a7a3c73e9876ec14a0268f3" + version "1.5.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" core-js@1.0.1: version "1.0.1" @@ -848,9 +868,9 @@ core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" -coveralls@^2.12.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.12.0.tgz#b3d064108e29728385b56e42fc2d119f43e0e517" +coveralls@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.0.tgz#df933876e8c6f478efb04f4d3ab70dc96b7e5a8e" dependencies: js-yaml "3.6.1" lcov-parse "0.0.10" @@ -1170,8 +1190,8 @@ esdoc@^0.5.2: taffydb "2.7.2" eslint@^3.0.0: - version "3.18.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.18.0.tgz#647e985c4ae71502d20ac62c109f66d5104c8a4b" + version "3.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" dependencies: babel-code-frame "^6.16.0" chalk "^1.1.3" @@ -1210,10 +1230,10 @@ eslint@^3.0.0: user-home "^2.0.0" espree@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.0.tgz#41656fa5628e042878025ef467e78f125cb86e1d" + version "3.4.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.1.tgz#28a83ab4aaed71ed8fe0f5efe61b76a05c13c4d2" dependencies: - acorn "4.0.4" + acorn "^5.0.1" acorn-jsx "^3.0.0" esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: @@ -1705,10 +1725,11 @@ gulp-plumber@^1.1.0: gulp-util "^3" through2 "^2" -gulp-sourcemaps@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.4.1.tgz#8f65dc5c0d07b2fd5c88bc60ec7f13e56716bf74" +gulp-sourcemaps@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.5.1.tgz#773b6d7c3b25eeff64b3cb34c091681aeb89fea6" dependencies: + "@gulp-sourcemaps/map-sources" "1.X" acorn "4.X" convert-source-map "1.X" css "2.X" @@ -1716,7 +1737,7 @@ gulp-sourcemaps@^2.4.1: detect-newline "2.X" graceful-fs "4.X" source-map "0.X" - strip-bom "3.X" + strip-bom-string "1.X" through2 "2.X" vinyl "1.X" @@ -1865,8 +1886,8 @@ ice-cap@0.0.4: color-logger "0.0.3" ignore@^3.2.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.6.tgz#26e8da0644be0bb4cb39516f6c79f0e0f4ffe48c" + version "3.2.7" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.7.tgz#4810ca5f1d8eca5595213a34b94f2eb4ed926bbd" imurmurhash@^0.1.4: version "0.1.4" @@ -1910,8 +1931,8 @@ inquirer@^0.12.0: through "^2.3.6" interpret@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" + version "1.0.2" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.2.tgz#f4f623f0bb7122f15f5717c8e254b8161b5c5b2d" invariant@^2.2.0: version "2.2.2" @@ -2563,8 +2584,10 @@ nopt@3.x: abbrev "1" normalize-path@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" nth-check@~1.0.1: version "1.0.1" @@ -2796,15 +2819,15 @@ readable-stream@1.1, readable-stream@~1.1.9: string_decoder "~0.10.x" readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2: - version "2.2.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.6.tgz#8b43aed76e71483938d12a8d46c6cf1a00b1f816" + version "2.2.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.8.tgz#ad28b686f3554c73d39bc32347fa058356624705" dependencies: - buffer-shims "^1.0.0" + buffer-shims "~1.0.0" core-util-is "~1.0.0" inherits "~2.0.1" isarray "~1.0.0" process-nextick-args "~1.0.6" - string_decoder "~0.10.x" + string_decoder "~1.0.0" util-deprecate "~1.0.1" readable-stream@~2.0.5: @@ -2840,9 +2863,9 @@ regenerator-runtime@^0.10.0: version "0.10.3" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" -regenerator-transform@0.9.8: - version "0.9.8" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.8.tgz#0f88bb2bc03932ddb7b6b7312e68078f01026d6c" +regenerator-transform@0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" dependencies: babel-runtime "^6.18.0" babel-types "^6.19.0" @@ -2873,6 +2896,10 @@ regjsparser@^0.1.4: dependencies: jsesc "~0.5.0" +remove-trailing-separator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" @@ -3177,6 +3204,12 @@ string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" +string_decoder@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" + dependencies: + buffer-shims "~1.0.0" + stringify-object@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.0.tgz#94370a135e41bc048358813bf99481f1315c6aa6" @@ -3195,9 +3228,9 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" -strip-bom@3.X, strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" +strip-bom-string@1.X: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" strip-bom@^1.0.0: version "1.0.0" @@ -3206,6 +3239,10 @@ strip-bom@^1.0.0: first-chunk-stream "^1.0.0" is-utf8 "^0.2.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -3335,8 +3372,8 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" uglify-js@^2.6: - version "2.8.16" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.16.tgz#d286190b6eefc6fd65eb0ecac6551e0b0e8839a4" + version "2.8.21" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.21.tgz#1733f669ae6f82fc90c7b25ec0f5c783ee375314" dependencies: source-map "~0.5.1" yargs "~3.10.0" @@ -3378,8 +3415,8 @@ uuid@^3.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" v8flags@^2.0.2: - version "2.0.11" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" + version "2.0.12" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.12.tgz#73235d9f7176f8e8833fb286795445f7938d84e5" dependencies: user-home "^1.1.1"