@test-runner/web
Version:
170 lines (151 loc) • 3.43 kB
JavaScript
/**
* An isomorphic, load-anywhere JavaScript class for building [composite structures](https://en.wikipedia.org/wiki/Composite_pattern). Suitable for use as a super class or mixin.
* @module composite-class
* @example
* const Composite = require('composite-class')
*/
const _children = new WeakMap()
const _parent = new WeakMap()
/**
* @alias module:composite-class
*/
class Composite {
/**
* Children
* @type {Array}
*/
get children () {
if (_children.has(this)) {
return _children.get(this)
} else {
_children.set(this, [])
return _children.get(this)
}
}
set children (val) {
_children.set(this, val)
}
/**
* Parent
* @type {Composite}
*/
get parent () {
return _parent.get(this)
}
set parent (val) {
_parent.set(this, val)
}
/**
* Add a child
* @returns {Composite}
*/
add (child) {
if (!(isComposite(child))) throw new Error('can only add a Composite instance')
child.parent = this
this.children.push(child)
return child
}
/**
* @param {Composite} child - the child node to append
* @returns {Composite}
*/
append (child) {
if (!(child instanceof Composite)) throw new Error('can only add a Composite instance')
child.parent = this
this.children.push(child)
return child
}
/**
* @param {Composite} child - the child node to prepend
* @returns {Composite}
*/
prepend (child) {
if (!(child instanceof Composite)) throw new Error('can only add a Composite instance')
child.parent = this
this.children.unshift(child)
return child
}
/**
* @param {Composite} child - the child node to remove
* @returns {Composite}
*/
remove (child) {
return this.children.splice(this.children.indexOf(child), 1)
}
/**
* depth level in the tree, 0 being root.
* @returns {number}
*/
level () {
let count = 0
function countParent (composite) {
if (composite.parent) {
count++
countParent(composite.parent)
}
}
countParent(this)
return count
}
/**
* @returns {number}
*/
getDescendentCount () {
return Array.from(this).length
}
/**
* prints a tree using the .toString() representation of each node in the tree
* @returns {string}
*/
tree () {
return Array.from(this).reduce((prev, curr) => {
return (prev += `${' '.repeat(curr.level())}- ${curr}\n`)
}, '')
}
/**
* Returns the root instance of this tree.
* @returns {Composite}
*/
root () {
function getRoot (composite) {
return composite.parent ? getRoot(composite.parent) : composite
}
return getRoot(this)
}
/**
* default iteration strategy
*/
* [Symbol.iterator] () {
yield this
for (const child of this.children) {
yield * child
}
}
/**
* Used by node's `util.inspect`.
*/
inspect (depth) {
const clone = Object.assign({}, this)
delete clone.parent
return clone
}
/**
* Returns an array of ancestors
* @return {Composite[]}
*/
parents () {
const output = []
function addParent (node) {
if (node.parent) {
output.push(node.parent)
addParent(node.parent)
}
}
addParent(this)
return output
}
}
function isComposite (item) {
return item && item.children && item.add && item.level && item.root
}
export default Composite