const merge = require('deepmerge'); const Chainable = require('./Chainable'); module.exports = class extends Chainable { constructor(parent) { super(parent); this.store = new Map(); } extend(methods) { this.shorthands = methods; methods.forEach(method => { this[method] = value => this.set(method, value); }); return this; } clear() { this.store.clear(); return this; } delete(key) { this.store.delete(key); return this; } order() { const entries = [...this.store].reduce((acc, [key, value]) => { acc[key] = value; return acc; }, {}); const names = Object.keys(entries); const order = [...names]; names.forEach(name => { if (!entries[name]) { return; } const { __before, __after } = entries[name]; if (__before && order.includes(__before)) { order.splice(order.indexOf(name), 1); order.splice(order.indexOf(__before), 0, name); } else if (__after && order.includes(__after)) { order.splice(order.indexOf(name), 1); order.splice(order.indexOf(__after) + 1, 0, name); } }); return { entries, order }; } entries() { const { entries, order } = this.order(); if (order.length) { return entries; } return undefined; } values() { const { entries, order } = this.order(); return order.map(name => entries[name]); } get(key) { return this.store.get(key); } getOrCompute(key, fn) { if (!this.has(key)) { this.set(key, fn()); } return this.get(key); } has(key) { return this.store.has(key); } set(key, value) { this.store.set(key, value); return this; } merge(obj, omit = []) { Object.keys(obj).forEach(key => { if (omit.includes(key)) { return; } const value = obj[key]; if ( (!Array.isArray(value) && typeof value !== 'object') || value === null || !this.has(key) ) { this.set(key, value); } else { this.set(key, merge(this.get(key), value)); } }); return this; } clean(obj) { return Object.keys(obj).reduce((acc, key) => { const value = obj[key]; if (value === undefined) { return acc; } if (Array.isArray(value) && !value.length) { return acc; } if ( Object.prototype.toString.call(value) === '[object Object]' && !Object.keys(value).length ) { return acc; } acc[key] = value; return acc; }, {}); } when( condition, whenTruthy = Function.prototype, whenFalsy = Function.prototype ) { if (condition) { whenTruthy(this); } else { whenFalsy(this); } return this; } };