flipchain
Version:
core chaining library, heavily based on [webpack-chain](https://github.com/mozilla-rpweb/webpack-chain)
249 lines (226 loc) • 6.26 kB
JavaScript
const arrToObj = require('arr-to-obj')
const ChainedMap = require('./ChainedMap')
const Chainable = require('./Chainable')
const firstToUpper = str => str.charAt(0).toUpperCase() + str.slice(1)
const addPrefix = (string, prefix) => prefix + firstToUpper(string)
function removePrefix(string, prefix) {
if (string.indexOf(prefix) === 0) string = string.slice(prefix.length)
return string.charAt(0).toLowerCase() + string.slice(1)
}
// @TODO: extendBool which would add `no` firstChar.toUpperCase() + rest()
//
// maybe was doing this to bind the prefix variable?
// this.extendWith(methods.map((method) => (0, addPrefix)(method, prefix)), !val, prefix)
class ChainedMapExtendable extends ChainedMap {
constructor(parent) {
super(parent)
if (parent && parent.has && parent.has('debug')) {
this.debug(parent.get('debug'))
}
else {
this.debug(false)
}
}
// remove...
fromAnd(obj) {
let p = this.merge(obj).parent
while (p.parent) {
p = p.parent
}
return p
}
/**
* @param {boolean} [should=true]
* @return {Chainable}
*/
debug(should = true) {
return this.set('debug', should)
}
/**
* @see ChainedMapExtendable.parent
* @param {Array<Object>} decorations
* @return {ChainedMapExtendable}
*/
decorateParent(decorations) {
if (!this.decorated) this.decorated = new ChainedMap(this.parent)
decorations.forEach(decoration => {
const method = decoration.method
const returnee = decoration.return || this.parent
const key = decoration.key || method
this.parent[method] = data => {
this.set(key, data)
return returnee
}
})
return this
}
/**
* @param {Array<string>} methods
* @param {string} name
* @param {Boolean} [thisArg=null]
* @return {ChainedMap}
*/
extendAlias(methods, name, thisArg = null) {
methods.forEach(method => (this[method] = this[name].bind(thisArg || this)))
return this
}
/**
* @param {string} name
* @param {Object} Chain
* @return {ChainedMapExtendable}
*/
addChain(name, Chain) {
// making name available as a property on chainable
if (typeof name !== 'string') Chain = name
const chained = new Chain(this)
name = chained.name || name
this[name] = chained
this.chains.push(name)
return this
}
/**
* @see ChainedMapExtendable.extendPrefixed
* @param {Array<string>} methods
* @param {any} val
* @param {string} [prefix='no']
* @return {ChainedMapExtendable}
*/
extendBool(methods, val, prefix = 'no') {
this.extendPrefixed(methods, !val, prefix)
return this
}
/**
* @param {Array<string>} methods
* @param {any} val
* @param {string} [prefix='no']
* @param {string} [inverseValue='todo']
* @return {ChainedMapExtendable}
*/
extendPrefixed(methods, val, prefix = 'no', inverseValue = 'todo') {
this.extendWith(methods, val)
this.extendWith(
methods.map(method => addPrefix(method, prefix)),
!val,
prefix
)
return this
}
/**
* @param {Array<string>} methods
* @param {any} val
* @param {string} [prefix]
* @return {ChainedMapExtendable}
*/
extendWith(methods, val, prefix) {
const objMethods = arrToObj(methods, val)
const keys = Object.keys(objMethods)
this.shorthands = [...this.shorthands, ...keys]
keys.forEach(method => {
// value = objMethods[method]
this[method] = value => {
if (value === undefined || value === null) value = val
if (prefix) return this.set(removePrefix(method, prefix), value)
return this.set(method, value)
}
})
return this
}
/**
* @see ChainedMapExtendable.extendWith
* @param {Array<string>} methods
* @return {ChainedMapExtendable}
*/
extendFalse(methods) {
this.extendWith(methods, false)
return this
}
/**
* @see ChainedMapExtendable.extendWith
* @param {Array<string>} methods
* @return {ChainedMapExtendable}
*/
extendTrue(methods) {
this.extendWith(methods, true)
return this
}
/**
* @description
* extend with string types of izz
* if it is that type, good, otherwise, nope.
*
* @TODO finish this
* @see izz
*
* @param {Array<string>} methods
* @param {any} type
* @param {string | any} [msg=null]
* @return {ChainedMap}
*/
extendType(methods, type, msg = null) {
const is = require('izz')
methods.forEach(method => {
this.shorthands.push(method)
this[method] = value => {
if (!is[type](value)) {
if (msg) console.log(msg)
return this
}
this.set(method, value)
return this
}
})
return this
}
/**
* @description when called, increments the value
* @param {Array<string>} methods
* @return {ChainedMap}
*/
extendIncrement(methods) {
// every time it is called, just increment
// add to this.shorthands
methods.forEach(method => {
this.shorthands.push(method)
this[method] = () => {
let value = (this.get(method) | 0) + 1
this.set(method, value)
return this
}
})
return this
}
/**
* @description uses an object, loops through keys, adds method
* @see ChainedMapExtendable.shorthands
*
* @param {Object} methods
* @return {ChainedMap}
*/
extendDefault(methods) {
this.shorthands = [...this.shorthands, ...methods]
Object.keys(methods).forEach(method => {
this[method] = (value = methods[method]) => this.set(method, value)
})
return this
}
}
/**
* @desc function callable ChainedMapExtendable
* @param {Chainable | Object | null | *} [obj=null] parent when chainable, or `.from`
* @param {Object | null | *} [from={}] `.from` when `obj` is `chainable`
* @return {ChainedMapExtendable}
*/
function Chainables(obj = null, from = {}) {
if (
obj &&
(obj instanceof Chainable ||
obj instanceof Chainables ||
obj.store !== undefined)
) {
const chain = new ChainedMapExtendable(obj)
if (from !== null) chain.from(from)
return chain
}
return new ChainedMapExtendable().from(obj)
}
module.exports = ChainedMapExtendable