data-store
Advanced tools
Comparing version 1.0.0 to 2.0.0
766
index.js
'use strict'; | ||
/** | ||
* Module dependencies | ||
*/ | ||
const fs = require('fs'); | ||
const os = require('os'); | ||
const path = require('path'); | ||
const assert = require('assert'); | ||
const flatten = (...args) => [].concat.apply([], args); | ||
const unique = arr => arr.filter((v, i) => arr.indexOf(v) === i); | ||
var path = require('path'); | ||
var util = require('util'); | ||
var base = require('cache-base'); | ||
var Base = base.namespace('cache'); | ||
var debug = require('debug')('data-store'); | ||
var proto = Base.prototype; | ||
var utils = require('./utils'); | ||
/** | ||
* Expose `Store` | ||
*/ | ||
module.exports = Store; | ||
/** | ||
* Initialize a new `Store` with the given `name` | ||
* and `options`. | ||
* Initialize a new `Store` with the given `name`, `options` and `default` data. | ||
* | ||
* ```js | ||
* var store = require('data-store')('abc'); | ||
* const store = require('data-store')('abc'); | ||
* //=> '~/data-store/a.json' | ||
* | ||
* var store = require('data-store')('abc', { | ||
* cwd: 'test/fixtures' | ||
* }); | ||
* const store = require('data-store')('abc', { cwd: 'test/fixtures' }); | ||
* //=> './test/fixtures/abc.json' | ||
* ``` | ||
* | ||
* @param {String} `name` Store name to use for the basename of the `.json` file. | ||
* @param {Object} `options` | ||
* @param {String} `options.cwd` Current working directory for storage. If not defined, the user home directory is used, based on OS. This is the only option currently, other may be added in the future. | ||
* @param {Number} `options.indent` Number passed to `JSON.stringify` when saving the data. Defaults to `2` if `null` or `undefined` | ||
* @name Store | ||
* @param {string} `name` Store name to use for the basename of the `.json` file. | ||
* @param {object} `options` See all [available options](#options). | ||
* @param {object} `defaults` An object to initialize the store with. | ||
* @api public | ||
*/ | ||
function Store(name, options) { | ||
if (!(this instanceof Store)) { | ||
return new Store(name, options); | ||
class Store { | ||
constructor(name, options = {}, defaults = {}) { | ||
if (typeof name !== 'string') { | ||
defaults = options; | ||
options = name || {}; | ||
name = options.name; | ||
} | ||
assert.equal(typeof name, 'string', 'expected store name to be a string'); | ||
const { debounce = 5, indent = 2, home, base } = options; | ||
this.name = name; | ||
this.options = options; | ||
this.defaults = defaults || options.default; | ||
this.indent = indent; | ||
this.debounce = debounce; | ||
this.home = home || process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'); | ||
this.base = base || path.join(this.home, 'data-store'); | ||
this.path = this.options.path || path.join(this.base, this.name + '.json'); | ||
this._data = null; | ||
} | ||
if (typeof name !== 'string') { | ||
options = name; | ||
name = null; | ||
/** | ||
* Assign `value` to `key` and save to the file system. Can be a key-value pair, | ||
* array of objects, or an object. | ||
* | ||
* ```js | ||
* // key, value | ||
* store.set('a', 'b'); | ||
* //=> {a: 'b'} | ||
* | ||
* // extend the store with an object | ||
* store.set({a: 'b'}); | ||
* //=> {a: 'b'} | ||
* ``` | ||
* @name .set | ||
* @param {string} `key` | ||
* @param {any} `val` The value to save to `key`. Must be a valid JSON type: String, Number, Array or Object. | ||
* @return {object} `Store` for chaining | ||
* @api public | ||
*/ | ||
set(key, val) { | ||
if (isObject(key)) { | ||
Object.assign(this.data, key); | ||
} else { | ||
assert.equal(typeof key, 'string', 'expected key to be a string'); | ||
set(this.data, key, val); | ||
} | ||
this.save(); | ||
return this; | ||
} | ||
Base.call(this); | ||
this.isStore = true; | ||
this.options = options || {}; | ||
this.initStore(name); | ||
} | ||
/** | ||
* Add the given `value` to the array at `key`. Creates a new array if one | ||
* doesn't exist, and only adds unique values to the array. | ||
* | ||
* ```js | ||
* store.union('a', 'b'); | ||
* store.union('a', 'c'); | ||
* store.union('a', 'd'); | ||
* store.union('a', 'c'); | ||
* console.log(store.get('a')); | ||
* //=> ['b', 'c', 'd'] | ||
* ``` | ||
* @name .union | ||
* @param {string} `key` | ||
* @param {any} `val` The value to union to `key`. Must be a valid JSON type: String, Number, Array or Object. | ||
* @return {object} `Store` for chaining | ||
* @api public | ||
*/ | ||
/** | ||
* Inherit `Base` | ||
*/ | ||
union(key, ...rest) { | ||
assert.equal(typeof key, 'string', 'expected key to be a string'); | ||
let arr = this.get(key); | ||
if (arr == null) arr = []; | ||
if (!Array.isArray(arr)) arr = [arr]; | ||
this.set(key, unique(flatten(...arr, ...rest))); | ||
return this; | ||
} | ||
util.inherits(Store, Base); | ||
/** | ||
* Get the stored `value` of `key`. | ||
* | ||
* ```js | ||
* store.set('a', {b: 'c'}); | ||
* store.get('a'); | ||
* //=> {b: 'c'} | ||
* | ||
* store.get(); | ||
* //=> {a: {b: 'c'}} | ||
* ``` | ||
* @name .get | ||
* @param {string} `key` | ||
* @return {any} The value to store for `key`. | ||
* @api public | ||
*/ | ||
/** | ||
* Initialize store defaults | ||
*/ | ||
get(key) { | ||
assert.equal(typeof key, 'string', 'expected key to be a string'); | ||
return get(this.data, key); | ||
} | ||
Store.prototype.initStore = function(name) { | ||
this.name = name || utils.project(process.cwd()); | ||
this.cwd = utils.resolve(this.options.cwd || '~/.data-store'); | ||
this.path = this.options.path || path.resolve(this.cwd, this.name + '.json'); | ||
this.relative = path.relative(process.cwd(), this.path); | ||
/** | ||
* Returns `true` if the specified `key` has a value. | ||
* | ||
* ```js | ||
* store.set('a', 42); | ||
* store.set('c', null); | ||
* | ||
* store.has('a'); //=> true | ||
* store.has('c'); //=> true | ||
* store.has('d'); //=> false | ||
* ``` | ||
* @name .has | ||
* @param {string} `key` | ||
* @return {boolean} Returns true if `key` has | ||
* @api public | ||
*/ | ||
debug('Initializing store <%s>', this.path); | ||
has(key) { | ||
assert.equal(typeof key, 'string', 'expected key to be a string'); | ||
return typeof get(this.data, key) !== 'undefined'; | ||
} | ||
this.data = this.readFile(this.path); | ||
this.define('cache', utils.clone(this.data)); | ||
this.on('set', function() { | ||
this.save(); | ||
}.bind(this)); | ||
}; | ||
/** | ||
* Returns `true` if the specified `key` exists. | ||
* | ||
* ```js | ||
* store.set('a', 'b'); | ||
* store.set('b', false); | ||
* store.set('c', null); | ||
* store.set('d', true); | ||
* store.set('e', undefined); | ||
* | ||
* store.hasOwn('a'); //=> true | ||
* store.hasOwn('b'); //=> true | ||
* store.hasOwn('c'); //=> true | ||
* store.hasOwn('d'); //=> true | ||
* store.hasOwn('e'); //=> true | ||
* store.hasOwn('foo'); //=> false | ||
* ``` | ||
* @name .hasOwn | ||
* @param {string} `key` | ||
* @return {boolean} Returns true if `key` exists | ||
* @api public | ||
*/ | ||
/** | ||
* Create a namespaced "sub-store" that persists data to its file | ||
* in a sub-folder of the same directory as the "parent" store. | ||
* | ||
* ```js | ||
* store.create('foo'); | ||
* store.foo.set('a', 'b'); | ||
* console.log(store.foo.get('a')); | ||
* //=> 'b' | ||
* ``` | ||
* @param {String} `name` The name of the sub-store. | ||
* @param {Object} `options` | ||
* @return {Object} Returns the sub-store instance. | ||
* @api public | ||
*/ | ||
Store.prototype.create = function(name, options) { | ||
if (utils.isStore(this, name)) { | ||
return this[name]; | ||
hasOwn(key) { | ||
assert.equal(typeof key, 'string', 'expected key to be a string'); | ||
return hasOwn(this.data, key); | ||
} | ||
utils.validateName(this, name); | ||
var self = this; | ||
var cwd = path.join(path.dirname(this.path), this.name); | ||
var substore = new Store(name, { cwd: cwd }); | ||
this[name] = substore; | ||
/** | ||
* Delete one or more properties from the store. | ||
* | ||
* ```js | ||
* store.set('foo.bar', 'baz'); | ||
* console.log(store.data); //=> { foo: { bar: 'baz' } } | ||
* store.del('foo.bar'); | ||
* console.log(store.data); //=> { foo: {} } | ||
* store.del('foo'); | ||
* console.log(store.data); //=> {} | ||
* ``` | ||
* @name .del | ||
* @param {string|Array} `keys` One or more properties to delete. | ||
* @api public | ||
*/ | ||
substore.on('set', function(key, val) { | ||
self.set([name, key], val); | ||
}); | ||
del(key) { | ||
if (Array.isArray(key)) { | ||
for (const k of key) this.del(k); | ||
return this; | ||
} | ||
assert.equal(typeof key, 'string', 'expected key to be a string'); | ||
if (del(this.data, key)) { | ||
this.save(); | ||
} | ||
return this; | ||
} | ||
return substore; | ||
}; | ||
/** | ||
* Return a clone of the `store.data` object. | ||
* | ||
* ```js | ||
* console.log(store.clone()); | ||
* ``` | ||
* @name .clone | ||
* @return {object} | ||
* @api public | ||
*/ | ||
/** | ||
* Assign `value` to `key` and save to disk. Can be | ||
* a key-value pair or an object. | ||
* | ||
* ```js | ||
* // key, value | ||
* store.set('a', 'b'); | ||
* //=> {a: 'b'} | ||
* | ||
* // extend the store with an object | ||
* store.set({a: 'b'}); | ||
* //=> {a: 'b'} | ||
* | ||
* // extend the the given value | ||
* store.set('a', {b: 'c'}); | ||
* store.set('a', {d: 'e'}, true); | ||
* //=> {a: {b 'c', d: 'e'}} | ||
* | ||
* // overwrite the the given value | ||
* store.set('a', {b: 'c'}); | ||
* store.set('a', {d: 'e'}); | ||
* //=> {d: 'e'} | ||
* ``` | ||
* @name .set | ||
* @param {String} `key` | ||
* @param {any} `val` The value to save to `key`. Must be a valid JSON type: String, Number, Array or Object. | ||
* @return {Object} `Store` for chaining | ||
* @api public | ||
*/ | ||
clone() { | ||
return cloneDeep(this.data); | ||
} | ||
/** | ||
* Add or append an array of unique values to the given `key`. | ||
* | ||
* ```js | ||
* store.union('a', ['a']); | ||
* store.union('a', ['b']); | ||
* store.union('a', ['c']); | ||
* store.get('a'); | ||
* //=> ['a', 'b', 'c'] | ||
* ``` | ||
* | ||
* @param {String} `key` | ||
* @return {any} The array to add or append for `key`. | ||
* @api public | ||
*/ | ||
/** | ||
* Reset `store.data` to an empty object. | ||
* | ||
* ```js | ||
* store.clear(); | ||
* ``` | ||
* @name .clear | ||
* @return {undefined} | ||
* @api public | ||
*/ | ||
Store.prototype.union = function(key, val) { | ||
utils.union(this.cache, key, val); | ||
this.emit('union', key, val); | ||
this.save(); | ||
return this; | ||
}; | ||
clear() { | ||
this.data = {}; | ||
} | ||
/** | ||
* Get the stored `value` of `key`, or return the entire store | ||
* if no `key` is defined. | ||
* | ||
* ```js | ||
* store.set('a', {b: 'c'}); | ||
* store.get('a'); | ||
* //=> {b: 'c'} | ||
* | ||
* store.get(); | ||
* //=> {b: 'c'} | ||
* ``` | ||
* | ||
* @name .get | ||
* @param {String} `key` | ||
* @return {any} The value to store for `key`. | ||
* @api public | ||
*/ | ||
/** | ||
* Stringify the store. Takes the same arguments as `JSON.stringify`. | ||
* | ||
* ```js | ||
* console.log(store.json(null, 2)); | ||
* ``` | ||
* @name .json | ||
* @return {string} | ||
* @api public | ||
*/ | ||
/** | ||
* Returns `true` if the specified `key` has truthy value. | ||
* | ||
* ```js | ||
* store.set('a', 'b'); | ||
* store.set('c', null); | ||
* store.has('a'); //=> true | ||
* store.has('c'); //=> false | ||
* store.has('d'); //=> false | ||
* ``` | ||
* @name .has | ||
* @param {String} `key` | ||
* @return {Boolean} Returns true if `key` has | ||
* @api public | ||
*/ | ||
json(replacer = null, space = this.indent) { | ||
return JSON.stringify(this.data, replacer, space); | ||
} | ||
/** | ||
* Returns `true` if the specified `key` exists. | ||
* | ||
* ```js | ||
* store.set('a', 'b'); | ||
* store.set('b', false); | ||
* store.set('c', null); | ||
* store.set('d', true); | ||
* | ||
* store.hasOwn('a'); //=> true | ||
* store.hasOwn('b'); //=> true | ||
* store.hasOwn('c'); //=> true | ||
* store.hasOwn('d'); //=> true | ||
* store.hasOwn('foo'); //=> false | ||
* ``` | ||
* | ||
* @param {String} `key` | ||
* @return {Boolean} Returns true if `key` exists | ||
* @api public | ||
*/ | ||
/** | ||
* Calls [.writeFile()](#writefile) to persist the store to the file system, | ||
* after an optional [debounce](#options) period. This method should probably | ||
* not be called directly as it's used internally by other methods. | ||
* | ||
* ```js | ||
* store.save(); | ||
* ``` | ||
* @name .save | ||
* @return {undefined} | ||
* @api public | ||
*/ | ||
Store.prototype.hasOwn = function(key) { | ||
var val; | ||
if (key.indexOf('.') === -1) { | ||
val = this.cache.hasOwnProperty(key); | ||
} else { | ||
val = utils.hasOwn(this.cache, key); | ||
save() { | ||
if (!this.debounce) return this.writeFile(); | ||
if (this.save.debounce) return; | ||
this.save.debounce = setTimeout(() => this.writeFile(), this.debounce); | ||
} | ||
return val; | ||
}; | ||
/** | ||
* Persist the store to disk. | ||
* | ||
* ```js | ||
* store.save(); | ||
* ``` | ||
* @param {String} `dest` Optionally define an alternate destination file path. | ||
* @api public | ||
*/ | ||
/** | ||
* Delete the store from the file system. | ||
* | ||
* ```js | ||
* store.unlink(); | ||
* ``` | ||
* @name .unlink | ||
* @return {undefined} | ||
* @api public | ||
*/ | ||
Store.prototype.save = function(dest) { | ||
this.data = this.cache; | ||
writeJson(dest || this.path, this.cache, this.options.indent); | ||
return this; | ||
}; | ||
unlink() { | ||
let wait = 0; | ||
/** | ||
* Clear in-memory cache. | ||
* | ||
* ```js | ||
* store.clear(); | ||
* ``` | ||
* @api public | ||
*/ | ||
if (this.unlink.clear) this.unlink.clear(); | ||
Store.prototype.clear = function() { | ||
this.cache = {}; | ||
this.data = {}; | ||
return this; | ||
}; | ||
const debounce = () => { | ||
const timeout = setTimeout(() => { | ||
if (this.save.debounce) { | ||
debounce(); | ||
} else { | ||
this.deleteFile(); | ||
} | ||
}, wait++); | ||
return () => clearTimeout(timeout); | ||
}; | ||
/** | ||
* Delete `keys` from the store, or delete the entire store | ||
* if no keys are passed. A `del` event is also emitted for each key | ||
* deleted. | ||
* | ||
* **Note that to delete the entire store you must pass `{force: true}`** | ||
* | ||
* ```js | ||
* store.del(); | ||
* | ||
* // to delete paths outside cwd | ||
* store.del({force: true}); | ||
* ``` | ||
* | ||
* @param {String|Array|Object} `keys` Keys to remove, or options. | ||
* @param {Object} `options` | ||
* @api public | ||
*/ | ||
Store.prototype.del = function(keys, options) { | ||
var isArray = Array.isArray(keys); | ||
if (typeof keys === 'string' || isArray) { | ||
keys = utils.arrayify(keys); | ||
} else { | ||
options = keys; | ||
keys = null; | ||
this.unlink.clear = debounce(); | ||
} | ||
options = options || {}; | ||
/** | ||
* Immediately write the store to the file system. This method should probably | ||
* not be called directly. Unless you are familiar with the inner workings of | ||
* the code it's recommended that you use .save() instead. | ||
* | ||
* ```js | ||
* store.writeFile(); | ||
* ``` | ||
* @name .writeFile | ||
* @return {undefined} | ||
*/ | ||
if (keys) { | ||
keys.forEach(function(key) { | ||
proto.del.call(this, key); | ||
}.bind(this)); | ||
this.save(); | ||
return this; | ||
writeFile() { | ||
if (this.save.debounce) { | ||
clearTimeout(this.save.debounce); | ||
this.save.debounce = null; | ||
} | ||
if (!this.saved) mkdir(path.dirname(this.path), this.options.mkdir); | ||
this.saved = true; | ||
fs.writeFileSync(this.path, this.json(), { mode: 0o0600 }); | ||
} | ||
if (options.force !== true) { | ||
throw new Error('options.force is required to delete the entire cache.'); | ||
/** | ||
* Immediately delete the store from the file system. This method should probably | ||
* not be called directly. Unless you are familiar with the inner workings of | ||
* the code, it's recommended that you use .unlink() instead. | ||
* | ||
* ```js | ||
* store.deleteFile(); | ||
* ``` | ||
* @name .deleteFile | ||
* @return {undefined} | ||
*/ | ||
deleteFile() { | ||
if (this.unlink.clear) this.unlink.clear(); | ||
tryUnlink(this.path); | ||
} | ||
keys = Object.keys(this.cache); | ||
this.clear(); | ||
/** | ||
* Load the store. | ||
* @return {object} | ||
*/ | ||
// if no keys are passed, delete the entire store | ||
utils.del.sync(this.path, options); | ||
keys.forEach(function(key) { | ||
this.emit('del', key); | ||
}.bind(this)); | ||
return this; | ||
}; | ||
load() { | ||
try { | ||
return JSON.parse(fs.readFileSync(this.path)); | ||
} catch (err) { | ||
if (err.code === 'EACCES') { | ||
err.message += '\ndata-store does not have permission to load this file\n'; | ||
throw err; | ||
} | ||
if (err.code === 'ENOENT' || err.name === 'SyntaxError') { | ||
this._data = {}; | ||
return {}; | ||
} | ||
} | ||
} | ||
/** | ||
* Returns an array of all Store properties. | ||
*/ | ||
/** | ||
* Getter/setter for the `store.data` object, which holds the values | ||
* that are persisted to the file system. | ||
* | ||
* ```js | ||
* console.log(store.data); //=> {} | ||
* store.data.foo = 'bar'; | ||
* console.log(store.get('foo')); //=> 'bar' | ||
* ``` | ||
* @name .data | ||
* @return {object} | ||
*/ | ||
utils.define(Store.prototype, 'keys', { | ||
configurable: true, | ||
enumerable: true, | ||
set: function(keys) { | ||
utils.define(this, 'keys', keys); | ||
}, | ||
get: function fn() { | ||
if (fn.keys) return fn.keys; | ||
fn.keys = []; | ||
for (var key in this) fn.keys.push(key); | ||
return fn.keys; | ||
set data(val) { | ||
this._data = val; | ||
this.save(); | ||
} | ||
}); | ||
get data() { | ||
this._data = this._data || this.load(); | ||
if (!this.saved) { | ||
this._data = Object.assign({}, this.defaults, this._data); | ||
} | ||
return this._data; | ||
} | ||
} | ||
/** | ||
* Define a non-enumerable property on the instance. | ||
* | ||
* @param {String} `key` | ||
* @param {any} `value` | ||
* @return {Object} Returns the instance for chaining. | ||
* @api public | ||
* Utils | ||
*/ | ||
Store.prototype.define = function(key, value) { | ||
utils.define(this, key, value); | ||
return this; | ||
}; | ||
const mode = opts => opts.mode || 0o777 & ~process.umask(); | ||
const strip = str => str.replace(/\\(?=\.)/g, ''); | ||
const split = str => str.split(/(?<!\\)\./).map(strip); | ||
/** | ||
* Read JSON files. | ||
* | ||
* @param {String} `fp` | ||
* @return {Object} | ||
* Create a directory and any intermediate directories that might exist. | ||
*/ | ||
Store.prototype.readFile = function(filepath) { | ||
function mkdir(dirname, options = {}) { | ||
assert.equal(typeof dirname, 'string', 'expected dirname to be a string'); | ||
const opts = Object.assign({ cwd: process.cwd(), fs }, options); | ||
const segs = path.relative(opts.cwd, dirname).split(path.sep); | ||
const make = dir => fs.mkdirSync(dir, mode(opts)); | ||
for (let i = 0; i <= segs.length; i++) { | ||
try { | ||
make((dirname = path.join(opts.cwd, ...segs.slice(0, i)))); | ||
} catch (err) { | ||
handleError(dirname, opts)(err); | ||
} | ||
} | ||
return dirname; | ||
} | ||
function handleError(dir, opts = {}) { | ||
return (err) => { | ||
if (err.code !== 'EEXIST' || path.dirname(dir) === dir || !opts.fs.statSync(dir).isDirectory()) { | ||
throw err; | ||
} | ||
}; | ||
} | ||
function tryUnlink(filepath) { | ||
try { | ||
var str = utils.fs.readFileSync(path.resolve(filepath), 'utf8'); | ||
this.loadedConfig = true; | ||
return JSON.parse(str); | ||
} catch (err) {} | ||
this.loadedConfig = false; | ||
return {}; | ||
}; | ||
fs.unlinkSync(filepath); | ||
} catch (err) { | ||
if (err.code !== 'ENOENT') { | ||
throw err; | ||
} | ||
} | ||
} | ||
function get(data = {}, prop = '') { | ||
return data[prop] == null | ||
? split(prop).reduce((acc, k) => acc && acc[strip(k)], data) | ||
: data[prop]; | ||
} | ||
function set(data = {}, prop = '', val) { | ||
return split(prop).reduce((acc, k, i, arr) => { | ||
let value = arr.length - 1 > i ? (acc[k] || {}) : val; | ||
if (!isObject(value) && i < arr.length - 1) value = {}; | ||
return (acc[k] = value); | ||
}, data); | ||
} | ||
function del(data = {}, prop = '') { | ||
if (!prop) return false; | ||
if (data.hasOwnProperty(prop)) { | ||
delete data[prop]; | ||
return true; | ||
} | ||
const segs = split(prop); | ||
const last = segs.pop(); | ||
const val = segs.length ? get(data, segs.join('.')) : data; | ||
if (isObject(val) && val.hasOwnProperty(last)) { | ||
delete val[last]; | ||
return true; | ||
} | ||
} | ||
function hasOwn(data = {}, prop = '') { | ||
if (!prop) return false; | ||
if (data.hasOwnProperty(prop)) return true; | ||
if (prop.indexOf('.') === -1) return false; | ||
const segs = split(prop); | ||
const last = segs.pop(); | ||
const val = segs.length ? get(data, segs.join('.')) : data; | ||
return isObject(val) && val.hasOwnProperty(last); | ||
} | ||
function isObject(val) { | ||
return typeOf(val) === 'object'; | ||
} | ||
/** | ||
* Synchronously write files to disk, also creating any | ||
* intermediary directories if they don't exist. | ||
* | ||
* @param {String} `dest` | ||
* @param {String} `str` | ||
* @param {Number} `indent` Indent passed to JSON.stringify (default 2) | ||
* Deeply clone plain objects and arrays. | ||
*/ | ||
function writeJson(dest, str, indent) { | ||
if (typeof indent === 'undefined' || indent === null) { | ||
indent = 2; | ||
} | ||
var dir = path.dirname(dest); | ||
try { | ||
if (!utils.fs.existsSync(dir)) { | ||
utils.mkdirp.sync(dir); | ||
function cloneDeep(value) { | ||
const obj = {}; | ||
switch (typeOf(value)) { | ||
case 'object': | ||
for (const key of Object.keys(value)) { | ||
obj[key] = cloneDeep(value[key]); | ||
} | ||
return obj; | ||
case 'array': | ||
return value.map(ele => cloneDeep(ele)); | ||
default: { | ||
return value; | ||
} | ||
utils.fs.writeFileSync(dest, JSON.stringify(str, null, indent)); | ||
} catch (err) { | ||
err.origin = __filename; | ||
err.reason = 'data-store cannot write to: ' + dest; | ||
throw new Error(err); | ||
} | ||
} | ||
function typeOf(val) { | ||
if (typeof val === 'string') return 'string'; | ||
if (Array.isArray(val)) return 'array'; | ||
if (val instanceof RegExp) { | ||
return 'regexp'; | ||
} | ||
if (val && typeof val === 'object') { | ||
return 'object'; | ||
} | ||
} | ||
/** | ||
* Expose `Store` | ||
*/ | ||
module.exports = Store; |
{ | ||
"name": "data-store", | ||
"description": "Easily get, set and persist config data.", | ||
"version": "1.0.0", | ||
"description": "Easily persist and load config data. No dependencies.", | ||
"version": "2.0.0", | ||
"homepage": "https://github.com/jonschlinkert/data-store", | ||
@@ -9,4 +9,5 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)", | ||
"Brian Woodward (https://twitter.com/doowb)", | ||
"Charlike Mike Reagent (https://i.am.charlike.online)", | ||
"Jon Schlinkert (http://twitter.com/jonschlinkert)" | ||
"Jamen Marz (jamenmarz.com)", | ||
"Jon Schlinkert (http://twitter.com/jonschlinkert)", | ||
"Olsten Larck (https://i.am.charlike.online)" | ||
], | ||
@@ -19,8 +20,7 @@ "repository": "jonschlinkert/data-store", | ||
"files": [ | ||
"index.js", | ||
"utils.js" | ||
"index.js" | ||
], | ||
"main": "index.js", | ||
"engines": { | ||
"node": ">=0.10.0" | ||
"node": ">=8" | ||
}, | ||
@@ -30,24 +30,6 @@ "scripts": { | ||
}, | ||
"dependencies": { | ||
"cache-base": "^1.0.0", | ||
"clone-deep": "^0.3.0", | ||
"debug": "^2.6.8", | ||
"define-property": "^1.0.0", | ||
"extend-shallow": "^2.0.1", | ||
"graceful-fs": "^4.1.11", | ||
"has-own-deep": "^0.1.4", | ||
"lazy-cache": "^2.0.2", | ||
"mkdirp": "^0.5.1", | ||
"project-name": "^0.2.6", | ||
"resolve-dir": "^1.0.0", | ||
"rimraf": "^2.6.1", | ||
"union-value": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"gulp": "^3.9.1", | ||
"gulp-eslint": "^3.0.1", | ||
"gulp-format-md": "^0.1.12", | ||
"gulp-istanbul": "^1.1.1", | ||
"gulp-mocha": "^3.0.1", | ||
"mocha": "^3.4.1" | ||
"delete": "^1.1.0", | ||
"gulp-format-md": "^1.0.0", | ||
"mocha": "^3.5.3" | ||
}, | ||
@@ -89,12 +71,9 @@ "keywords": [ | ||
"related": { | ||
"highlight": "base-store", | ||
"list": [ | ||
"base-store", | ||
"cache-base", | ||
"get-value", | ||
"set-value" | ||
"has-value", | ||
"set-value", | ||
"write" | ||
] | ||
}, | ||
"reflinks": [ | ||
], | ||
"lint": { | ||
@@ -101,0 +80,0 @@ "reflinks": true |
268
README.md
# data-store [![NPM version](https://img.shields.io/npm/v/data-store.svg?style=flat)](https://www.npmjs.com/package/data-store) [![NPM monthly downloads](https://img.shields.io/npm/dm/data-store.svg?style=flat)](https://npmjs.org/package/data-store) [![NPM total downloads](https://img.shields.io/npm/dt/data-store.svg?style=flat)](https://npmjs.org/package/data-store) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/data-store.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/data-store) | ||
> Easily get, set and persist config data. | ||
> Easily persist and load config data. No dependencies. | ||
You might also be interested in [base-store](https://github.com/node-base/base-store). | ||
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. | ||
## Table of Contents | ||
- [Install](#install) | ||
- [Usage example](#usage-example) | ||
- [API](#api) | ||
- [Options](#options) | ||
- [About](#about) | ||
@@ -26,11 +25,14 @@ | ||
By default a JSON file is created with the name of the store in the `~/.config/data-store/` directory. This is completely customizable via [options](#options). | ||
```js | ||
// default cwd is `~/data-store/` (in user-home) | ||
var store = require('data-store')('my-app'); | ||
const Store = require('data-store'); | ||
const store = new Store({ path: 'config.json' }); | ||
store | ||
.set('a', 'b') | ||
.set({c: 'd'}) | ||
.set('e.f', 'g') | ||
store.set('one', 'two'); | ||
console.log(store.data); //=> { one: 'two' } | ||
store.set('x.y.z', 'boom!'); | ||
store.set({ c: 'd' }); | ||
console.log(store.get('e.f')); | ||
@@ -40,6 +42,6 @@ //=> 'g' | ||
console.log(store.get()); | ||
//=> {name: 'app', data: {a: 'b', c: 'd', e: {f: 'g' }}} | ||
//=> { name: 'app', data: { a: 'b', c: 'd', e: { f: 'g' } } } | ||
console.log(store.data); | ||
//=> {a: 'b', c: 'd', e: {f: 'g'}} | ||
//=> { a: 'b', c: 'd', e: { f: 'g' } } | ||
``` | ||
@@ -49,12 +51,11 @@ | ||
### [Store](index.js#L42) | ||
### [Store](index.js#L27) | ||
Initialize a new `Store` with the given `name` and `options`. | ||
Initialize a new `Store` with the given `name`, `options` and `default` data. | ||
**Params** | ||
* `name` **{String}**: Store name to use for the basename of the `.json` file. | ||
* `options` **{Object}** | ||
* `options.cwd` **{String}**: Current working directory for storage. If not defined, the user home directory is used, based on OS. This is the only option currently, other may be added in the future. | ||
* `options.indent` **{Number}**: Number passed to `JSON.stringify` when saving the data. Defaults to `2` if `null` or `undefined` | ||
* `name` **{string}**: Store name to use for the basename of the `.json` file. | ||
* `options` **{object}**: See all [available options](#options). | ||
* `defaults` **{object}**: An object to initialize the store with. | ||
@@ -64,39 +65,18 @@ **Example** | ||
```js | ||
var store = require('data-store')('abc'); | ||
const store = require('data-store')('abc'); | ||
//=> '~/data-store/a.json' | ||
var store = require('data-store')('abc', { | ||
cwd: 'test/fixtures' | ||
}); | ||
const store = require('data-store')('abc', { cwd: 'test/fixtures' }); | ||
//=> './test/fixtures/abc.json' | ||
``` | ||
### [.create](index.js#L99) | ||
### [.set](index.js#L68) | ||
Create a namespaced "sub-store" that persists data to its file in a sub-folder of the same directory as the "parent" store. | ||
Assign `value` to `key` and save to the file system. Can be a key-value pair, array of objects, or an object. | ||
**Params** | ||
* `name` **{String}**: The name of the sub-store. | ||
* `options` **{Object}** | ||
* `returns` **{Object}**: Returns the sub-store instance. | ||
**Example** | ||
```js | ||
store.create('foo'); | ||
store.foo.set('a', 'b'); | ||
console.log(store.foo.get('a')); | ||
//=> 'b' | ||
``` | ||
### [.set](index.js#L147) | ||
Assign `value` to `key` and save to disk. Can be a key-value pair or an object. | ||
**Params** | ||
* `key` **{String}** | ||
* `key` **{string}** | ||
* `val` **{any}**: The value to save to `key`. Must be a valid JSON type: String, Number, Array or Object. | ||
* `returns` **{Object}** `Store`: for chaining | ||
* `returns` **{object}** `Store`: for chaining | ||
@@ -113,22 +93,13 @@ **Example** | ||
//=> {a: 'b'} | ||
// extend the the given value | ||
store.set('a', {b: 'c'}); | ||
store.set('a', {d: 'e'}, true); | ||
//=> {a: {b 'c', d: 'e'}} | ||
// overwrite the the given value | ||
store.set('a', {b: 'c'}); | ||
store.set('a', {d: 'e'}); | ||
//=> {d: 'e'} | ||
``` | ||
### [.union](index.js#L163) | ||
### [.union](index.js#L98) | ||
Add or append an array of unique values to the given `key`. | ||
Add the given `value` to the array at `key`. Creates a new array if one doesn't exist, and only adds unique values to the array. | ||
**Params** | ||
* `key` **{String}** | ||
* `returns` **{any}**: The array to add or append for `key`. | ||
* `key` **{string}** | ||
* `val` **{any}**: The value to union to `key`. Must be a valid JSON type: String, Number, Array or Object. | ||
* `returns` **{object}** `Store`: for chaining | ||
@@ -138,16 +109,17 @@ **Example** | ||
```js | ||
store.union('a', ['a']); | ||
store.union('a', ['b']); | ||
store.union('a', ['c']); | ||
store.get('a'); | ||
//=> ['a', 'b', 'c'] | ||
store.union('a', 'b'); | ||
store.union('a', 'c'); | ||
store.union('a', 'd'); | ||
store.union('a', 'c'); | ||
console.log(store.get('a')); | ||
//=> ['b', 'c', 'd'] | ||
``` | ||
### [.get](index.js#L189) | ||
### [.get](index.js#L124) | ||
Get the stored `value` of `key`, or return the entire store if no `key` is defined. | ||
Get the stored `value` of `key`. | ||
**Params** | ||
* `key` **{String}** | ||
* `key` **{string}** | ||
* `returns` **{any}**: The value to store for `key`. | ||
@@ -163,13 +135,13 @@ | ||
store.get(); | ||
//=> {b: 'c'} | ||
//=> {a: {b: 'c'}} | ||
``` | ||
### [.has](index.js#L205) | ||
### [.has](index.js#L146) | ||
Returns `true` if the specified `key` has truthy value. | ||
Returns `true` if the specified `key` has a value. | ||
**Params** | ||
* `key` **{String}** | ||
* `returns` **{Boolean}**: Returns true if `key` has | ||
* `key` **{string}** | ||
* `returns` **{boolean}**: Returns true if `key` has | ||
@@ -179,10 +151,11 @@ **Example** | ||
```js | ||
store.set('a', 'b'); | ||
store.set('a', 42); | ||
store.set('c', null); | ||
store.has('a'); //=> true | ||
store.has('c'); //=> false | ||
store.has('c'); //=> true | ||
store.has('d'); //=> false | ||
``` | ||
### [.hasOwn](index.js#L226) | ||
### [.hasOwn](index.js#L174) | ||
@@ -193,4 +166,4 @@ Returns `true` if the specified `key` exists. | ||
* `key` **{String}** | ||
* `returns` **{Boolean}**: Returns true if `key` exists | ||
* `key` **{string}** | ||
* `returns` **{boolean}**: Returns true if `key` exists | ||
@@ -204,2 +177,3 @@ **Example** | ||
store.set('d', true); | ||
store.set('e', undefined); | ||
@@ -210,12 +184,13 @@ store.hasOwn('a'); //=> true | ||
store.hasOwn('d'); //=> true | ||
store.hasOwn('e'); //=> true | ||
store.hasOwn('foo'); //=> false | ||
``` | ||
### [.save](index.js#L246) | ||
### [.del](index.js#L195) | ||
Persist the store to disk. | ||
Delete one or more properties from the store. | ||
**Params** | ||
* `dest` **{String}**: Optionally define an alternate destination file path. | ||
* `keys` **{string|Array}**: One or more properties to delete. | ||
@@ -225,68 +200,104 @@ **Example** | ||
```js | ||
store.save(); | ||
store.set('foo.bar', 'baz'); | ||
console.log(store.data); //=> { foo: { bar: 'baz' } } | ||
store.del('foo.bar'); | ||
console.log(store.data); //=> { foo: {} } | ||
store.del('foo'); | ||
console.log(store.data); //=> {} | ||
``` | ||
### [.clear](index.js#L261) | ||
### [.clone](index.js#L218) | ||
Clear in-memory cache. | ||
Return a clone of the `store.data` object. | ||
* `returns` **{object}** | ||
**Example** | ||
```js | ||
console.log(store.clone()); | ||
``` | ||
### [.clear](index.js#L233) | ||
Reset `store.data` to an empty object. | ||
* `returns` **{undefined}** | ||
**Example** | ||
```js | ||
store.clear(); | ||
``` | ||
### [.del](index.js#L286) | ||
### [.json](index.js#L248) | ||
Delete `keys` from the store, or delete the entire store if no keys are passed. A `del` event is also emitted for each key deleted. | ||
Stringify the store. Takes the same arguments as `JSON.stringify`. | ||
**Note that to delete the entire store you must pass `{force: true}`** | ||
* `returns` **{string}** | ||
**Params** | ||
**Example** | ||
* `keys` **{String|Array|Object}**: Keys to remove, or options. | ||
* `options` **{Object}** | ||
```js | ||
console.log(store.json(null, 2)); | ||
``` | ||
### [.save](index.js#L265) | ||
Calls [.writeFile()](#writefile) to persist the store to the file system, after an optional [debounce](#options) period. This method should probably not be called directly as it's used internally by other methods. | ||
* `returns` **{undefined}** | ||
**Example** | ||
```js | ||
store.del(); | ||
// to delete paths outside cwd | ||
store.del({force: true}); | ||
store.save(); | ||
``` | ||
### [.define](index.js#L347) | ||
### [.unlink](index.js#L282) | ||
Define a non-enumerable property on the instance. | ||
Delete the store from the file system. | ||
**Params** | ||
* `returns` **{undefined}** | ||
* `key` **{String}** | ||
* `value` **{any}** | ||
* `returns` **{Object}**: Returns the instance for chaining. | ||
**Example** | ||
## About | ||
```js | ||
store.unlink(); | ||
``` | ||
### Related projects | ||
## Options | ||
* [base-store](https://www.npmjs.com/package/base-store): Plugin for getting and persisting config values with your base-methods application. Adds a 'store' object… [more](https://github.com/node-base/base-store) | [homepage](https://github.com/node-base/base-store "Plugin for getting and persisting config values with your base-methods application. Adds a 'store' object that exposes all of the methods from the data-store library. Also now supports sub-stores!") | ||
* [cache-base](https://www.npmjs.com/package/cache-base): Basic object cache with `get`, `set`, `del`, and `has` methods for node.js/javascript projects. | [homepage](https://github.com/jonschlinkert/cache-base "Basic object cache with `get`, `set`, `del`, and `has` methods for node.js/javascript projects.") | ||
* [get-value](https://www.npmjs.com/package/get-value): Use property paths (`a.b.c`) to get a nested value from an object. | [homepage](https://github.com/jonschlinkert/get-value "Use property paths (`a.b.c`) to get a nested value from an object.") | ||
* [set-value](https://www.npmjs.com/package/set-value): Create nested values and any intermediaries using dot notation (`'a.b.c'`) paths. | [homepage](https://github.com/jonschlinkert/set-value "Create nested values and any intermediaries using dot notation (`'a.b.c'`) paths.") | ||
| **Option** | **Type** | **Default** | **Description** | | ||
| --- | --- | --- | --- | | ||
| `debounce` | `number` | `undefined` | Milliseconds to delay writing the JSON file to the file system. This can make the store more performant by preventing multiple subsequent writes after calling `.set` or setting/getting `store.data`, but comes with the potential side effect that the config file will be outdated during the timeout. To get around this, use data-store's API to [(re-)load](#load) the file instead of directly reading the file (using `fs.readFile` for example). | | ||
| `indent` | `number | null` | `2` | The indent value to pass to `JSON.stringify()` when writing the file to the fs, or when [.json()](#json) is called | | ||
| `name` | `string` | `undefined` | The name to use for the store file stem (`name + '.json'` is the store's file name) | | ||
| `home` | `string` | `process.env.XDG_CONFIG_HOME` or `path.join(os.homedir(), '.config')` | The root home directory to use | | ||
| `base` | `string` | `path.join(home, 'data-store')` | The directory to use for data-store config files. This value is joined to `home` | | ||
| `path` | `string` | `...` | Absolute file path for the data-store JSON file. This is created by joining `base` to `name + '.json'`. Setting this value directly will override the `name`, `home` and `base` values. | | ||
### Contributing | ||
## About | ||
<details> | ||
<summary><strong>Contributing</strong></summary> | ||
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). | ||
### Contributors | ||
</details> | ||
| **Commits** | **Contributor** | | ||
| --- | --- | | ||
| 128 | [jonschlinkert](https://github.com/jonschlinkert) | | ||
| 3 | [doowb](https://github.com/doowb) | | ||
| 2 | [tunnckoCore](https://github.com/tunnckoCore) | | ||
<details> | ||
<summary><strong>Running Tests</strong></summary> | ||
### Building docs | ||
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: | ||
```sh | ||
$ npm install && npm test | ||
``` | ||
</details> | ||
<details> | ||
<summary><strong>Building docs</strong></summary> | ||
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ | ||
@@ -300,10 +311,22 @@ | ||
### Running tests | ||
</details> | ||
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: | ||
### Related projects | ||
```sh | ||
$ npm install && npm test | ||
``` | ||
You might also be interested in these projects: | ||
* [get-value](https://www.npmjs.com/package/get-value): Use property paths like 'a.b.c' to get a nested value from an object. Even works… [more](https://github.com/jonschlinkert/get-value) | [homepage](https://github.com/jonschlinkert/get-value "Use property paths like 'a.b.c' to get a nested value from an object. Even works when keys have dots in them (no other dot-prop library can do this!).") | ||
* [has-value](https://www.npmjs.com/package/has-value): Returns true if a value exists, false if empty. Works with deeply nested values using… [more](https://github.com/jonschlinkert/has-value) | [homepage](https://github.com/jonschlinkert/has-value "Returns true if a value exists, false if empty. Works with deeply nested values using object paths.") | ||
* [set-value](https://www.npmjs.com/package/set-value): Create nested values and any intermediaries using dot notation (`'a.b.c'`) paths. | [homepage](https://github.com/jonschlinkert/set-value "Create nested values and any intermediaries using dot notation (`'a.b.c'`) paths.") | ||
* [write](https://www.npmjs.com/package/write): Write data to a file, replacing the file if it already exists and creating any… [more](https://github.com/jonschlinkert/write) | [homepage](https://github.com/jonschlinkert/write "Write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Thin wrapper around node's native fs methods.") | ||
### Contributors | ||
| **Commits** | **Contributor** | | ||
| --- | --- | | ||
| 135 | [jonschlinkert](https://github.com/jonschlinkert) | | ||
| 3 | [doowb](https://github.com/doowb) | | ||
| 2 | [charlike-old](https://github.com/charlike-old) | | ||
| 1 | [jamen](https://github.com/jamen) | | ||
### Author | ||
@@ -313,8 +336,9 @@ | ||
* [github/jonschlinkert](https://github.com/jonschlinkert) | ||
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert) | ||
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert) | ||
* [GitHub Profile](https://github.com/jonschlinkert) | ||
* [Twitter Profile](https://twitter.com/jonschlinkert) | ||
### License | ||
Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). | ||
Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). | ||
Released under the [MIT License](LICENSE). | ||
@@ -324,2 +348,2 @@ | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on May 22, 2017._ | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on May 01, 2018._ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
31788
0
3
456
334
45034
3
1
- Removedcache-base@^1.0.0
- Removedclone-deep@^0.3.0
- Removeddebug@^2.6.8
- Removeddefine-property@^1.0.0
- Removedextend-shallow@^2.0.1
- Removedgraceful-fs@^4.1.11
- Removedhas-own-deep@^0.1.4
- Removedlazy-cache@^2.0.2
- Removedmkdirp@^0.5.1
- Removedproject-name@^0.2.6
- Removedresolve-dir@^1.0.0
- Removedrimraf@^2.6.1
- Removedunion-value@^1.0.0
- Removedarr-union@3.1.0(transitive)
- Removedassign-symbols@1.0.0(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedcache-base@1.0.1(transitive)
- Removedclone-deep@0.3.0(transitive)
- Removedcollection-visit@1.0.0(transitive)
- Removedcomponent-emitter@1.3.1(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedcwd@0.9.1(transitive)
- Removeddebug@2.6.9(transitive)
- Removeddefine-property@1.0.0(transitive)
- Removedexpand-tilde@1.2.22.0.2(transitive)
- Removedextend-shallow@2.0.13.0.2(transitive)
- Removedfile-name@0.1.0(transitive)
- Removedfind-file-up@0.1.3(transitive)
- Removedfind-pkg@0.1.2(transitive)
- Removedfor-in@0.1.81.0.2(transitive)
- Removedfor-own@1.0.0(transitive)
- Removedfs-exists-sync@0.1.0(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-value@2.0.6(transitive)
- Removedgit-config-path@1.0.1(transitive)
- Removedgit-repo-name@0.6.0(transitive)
- Removedglob@7.2.3(transitive)
- Removedglobal-modules@0.2.31.0.0(transitive)
- Removedglobal-prefix@0.1.51.0.2(transitive)
- Removedgraceful-fs@4.2.11(transitive)
- Removedhas-own-deep@0.1.4(transitive)
- Removedhas-value@0.3.11.0.0(transitive)
- Removedhas-values@0.1.41.0.0(transitive)
- Removedhasown@2.0.2(transitive)
- Removedhomedir-polyfill@1.0.3(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedini@1.3.8(transitive)
- Removedis-accessor-descriptor@1.0.1(transitive)
- Removedis-buffer@1.1.6(transitive)
- Removedis-data-descriptor@1.0.1(transitive)
- Removedis-descriptor@1.0.3(transitive)
- Removedis-extendable@0.1.11.0.1(transitive)
- Removedis-number@3.0.0(transitive)
- Removedis-plain-object@2.0.4(transitive)
- Removedis-windows@0.2.01.0.2(transitive)
- Removedisarray@1.0.0(transitive)
- Removedisexe@2.0.0(transitive)
- Removedisobject@2.1.03.0.1(transitive)
- Removedkind-of@2.0.13.2.24.0.0(transitive)
- Removedlazy-cache@0.2.71.0.42.0.2(transitive)
- Removedmap-visit@1.0.0(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmixin-object@2.0.1(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedms@2.0.0(transitive)
- Removedobject-visit@1.0.1(transitive)
- Removedonce@1.4.0(transitive)
- Removedos-homedir@1.0.2(transitive)
- Removedparse-git-config@1.1.1(transitive)
- Removedparse-passwd@1.0.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedproject-name@0.2.6(transitive)
- Removedremote-origin-url@0.5.3(transitive)
- Removedresolve-dir@0.1.11.0.1(transitive)
- Removedrimraf@2.7.1(transitive)
- Removedset-getter@0.1.1(transitive)
- Removedset-value@2.0.1(transitive)
- Removedshallow-clone@0.1.2(transitive)
- Removedsplit-string@3.1.0(transitive)
- Removedto-object-path@0.3.0(transitive)
- Removedunion-value@1.0.1(transitive)
- Removedunset-value@1.0.0(transitive)
- Removedwhich@1.3.1(transitive)
- Removedwrappy@1.0.2(transitive)