Home Reference Source

src/utils/isClassOrComponent.js

/**
 * 
 *  ██▓ ███▄    █  ██ ▄█▀     ██████  ██▓███   ██▓    ▄▄▄     ▄▄▄█████▓
 *  ▓██▒ ██ ▀█   █  ██▄█▒    ▒██    ▒ ▓██░  ██▒▓██▒   ▒████▄   ▓  ██▒ ▓▒
 *  ▒██▒▓██  ▀█ ██▒▓███▄░    ░ ▓██▄   ▓██░ ██▓▒▒██░   ▒██  ▀█▄ ▒ ▓██░ ▒░
 *  ░██░▓██▒  ▐▌██▒▓██ █▄      ▒   ██▒▒██▄█▓▒ ▒▒██░   ░██▄▄▄▄██░ ▓██▓ ░
 *  ░██░▒██░   ▓██░▒██▒ █▄   ▒██████▒▒▒██▒ ░  ░░██████▒▓█   ▓██▒ ▒██▒ ░
 *  ░▓  ░ ▒░   ▒ ▒ ▒ ▒▒ ▓▒   ▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░░ ▒░▓  ░▒▒   ▓▒█░ ▒ ░░
 *  ▒ ░░ ░░   ░ ▒░░ ░▒ ▒░   ░ ░▒  ░ ░░▒ ░     ░ ░ ▒  ░ ▒   ▒▒ ░   ░
 *  ▒ ░   ░   ░ ░ ░ ░░ ░    ░  ░  ░  ░░         ░ ░    ░   ▒    ░
 *  ░           ░ ░  ░            ░               ░  ░     ░  ░
 * 
 * LICENSE: MIT
 */
import React from 'react';
// import Window from 'window';
// import renderJSDOM from '../tests/renderers/jsdomRenderer';

// const window = new Window();

/**
 * Returns a boolean which indicates that the provided `classA` matches
 * the provided `refClass` and is `true` only when the following conditions
 * are met:
 * 
 * (1) `refClass` is a type (function), and
 * (2) `classA` is that exact same type (function).
 * 
 * It is an equality test that returns true for any
 * `refClass === classA` if and only if `refClass` and `classA` are
 * functions, as `class A {}` transpiles to a function declaration.
 * 
 * @param {*} refClass The reference class (function).
 * @param {*} classA The actual class (function).
 * @returns `true` when `refClass === classA` and both are functions,
 * `false` otherwise.
 */
const isExactClass = (refClass, classA) =>
    (
        typeof refClass === 'function'
        &&
        typeof classA === 'function'
    )
    &&
    (
        refClass === classA
    );

/**
 * Returns a boolean which indicates if the provided `derivedClassB` matches
 * the following true when the following two conditions are met:
 * `refClass` is a type and `derivedClassB` is a type extended from `refClass`.
 * 
 * @param {*} refClass The reference class (function).
 * @param {*} derivedClassB The actual derived class (function).
 * @returns `true` when `derivedClassB.prototype instanceof refBaseClass` and both are functions,
 * `false` otherwise.
 */
const isDerivedClass = (refBaseClass, derivedClassB) =>
    (
        typeof refBaseClass === 'function'
        &&
        typeof derivedClassB === 'function'
    )
    &&
    (
        derivedClassB.prototype instanceof refBaseClass
    );

/**
 * Returns a boolean which indicates that the provided `inst` matches
 * is an instance of the provided `refClass` class and is `true` only
 * when the following conditions are met:
 * 
 * (1) `refClass` is a type (function), and
 * (2) `inst` is an object which an instance of `refClass`
 * 
 * It is an equality test that returns true for any
 * `refClass === classA` if and only if `refClass` and `classA` are
 * functions, as `class A {}` transpiles to a function declaration.
 * 
 * @param {*} refClass The reference class (function).
 * @param {*} classA The actual class (function).
 * @returns `true` when `refClass === classA` and both are functions,
 * `false` otherwise.
 */
const isInstanceOf = (refClass, inst) =>
(
    typeof refClass === 'function'
    &&
    typeof inst === 'object'
)
&&
(
    inst instanceof refClass
);

/**
 * Returns a boolean which indicates that the provided `componentA` component
 * is a React Component as created by `React.createElement()` instantiated using
 * the  `refClass` type and is `true` only when the following conditions
 * are met:
 * 
 * (1) `refClass` is a type (function), and
 * (2) `componentA` is created using `React.createElement(refClass)`
 * 
 * @param {*} refClass The reference class (function).
 * @param {*} componentA The actual component (object).
 * @returns `true` when `refClass === classA` and both are functions,
 * `false` otherwise.
 */
const isExactComponent = (refClass, componentA) =>
(
    typeof refClass === 'function'
    &&
    typeof componentA === 'object'
)
&&
(
    React.isValidElement(componentA)
)
&&
(
    isExactClass(refClass, componentA.type)
);

/**
 * Returns a boolean which indicates that the provided `derivedComponentB` component
 * is a React Component as created by `React.createElement()` instantiated using
 * the `refBaseClass` base type and is `true` only when the following conditions
 * are met:
 * 
 * (1) `refBaseClass` is a type (function), and
 * (2) `refBaseClass` has been extended to a secondary class, such as `class B extends A {}`
 * (3) `derivedComponentB` is created using `React.createElement(B)`
 * (4) You are interested in seeing if this Component element is of type derived from `A`
 * 
 * @param {*} refBaseClass The reference base class (function).
 * @param {*} componentA The actual component (object).
 * @returns `true` when `refClass === classA` and both are functions,
 * `false` otherwise.
 */
const isDerivedComponent = (refBaseClass, derivedComponentB) =>
(
    typeof refBaseClass === 'function'
    &&
    typeof derivedComponentB === 'object'
)
&&
(
    React.isValidElement(derivedComponentB)
)
&&
(
    isDerivedClass(refBaseClass, derivedComponentB.type)
);

/**
 * Returns a boolean which indicates if the provided anyInst matches any
 * of the following conditions (given class A extends React.Component {...}
 * and class B extends class A {...}):
 * 
 * (1) a reference to refClass (A === A),
 * 
 * (2) an instance of refClass (new A()),
 * 
 * (3) a React Element provided refClass as the type (React.createElement(A))
 * 
 * TODO: (4) a DOM Node which has been rendered with a React Element, type refClass
 * 
 * (5) a reference to a derived class (B === A whereas class B extends A {...})
 * 
 * (6) an instance of a derived class (new B() given class B extends A {...})
 * 
 * (7) a React Element with a derived class as the type (React.createElement(B))
 * 
 * TODO: (8) a DOM Node which has been rendered with a React Element, type derived refClass
 * 
 * @param {*} refClass The reference type as defined in a class declaration.
 * @param {*} inst Anything really, but mostly instances of things.
 * @returns A boolean indicating whether inst is a reference to refClass, a reference to
 * a class derived from refClass, an instance of refClass, an instance of a derived class,
 * or a React Element of type refClass, or a React Element of a derived refClass,
 * or a mounted DOM Node.
 */
const isClassOrComponent = (refClass, anyInst) =>
    // condition 1
    isExactClass(refClass, anyInst) ||
    // condition 5
    isDerivedClass(refClass, anyInst) ||
    // condition 2 and condition 6
    isInstanceOf(refClass, anyInst) ||
    // condition 3
    isExactComponent(refClass, anyInst) ||
    // condition 7
    isDerivedComponent(refClass, anyInst);
    // condition 4
    // TODO: isClassOrComponent condition 4 detection
    // condition 8
    // TODO: isClassOrComponent condition 8 detection

/**
 * Returns a function in the format `(anyInst) => boolean` which returns a boolean
 * which indicates if the provided `anyInst` matches any of the following conditions
 * (given `class A extends React.Component {...}` and `class B extends class A {...}`),
 * where A refers to the type provided in `refClass`:
 * 
 * (1) a reference to `refClass` (`A === A`),
 * 
 * (2) an instance of `refClass` (`new A()`),
 * 
 * (3) a React Element provided `refClass` as the type (`React.createElement(A)`)
 * 
 * TODO: (4) a DOM Node which has been rendered with a React Element, type `refClass`
 * 
 * (5) a reference to a derived class (`B === A` whereas `class B extends A {...}`)
 * 
 * (6) an instance of a derived class (`new B()` given `class B extends A {...}`)
 * 
 * (7) a React Element with a derived class as the type (`React.createElement(B)`)
 * 
 * TODO: (8) a DOM Node which has been rendered with a React Element, type derived from `refClass`
 * 
 * @param {*} refClass The reference type as defined in a class declaration.
 * @returns A function of the type `(anyInst) => boolean` which returns a boolean
 * indicating whether `inst` is a reference to `refClass`, a reference to a class derived
 * from `refClass`, an instance of `refClass`, an instance of a derived class,
 * or a React Element of type `refClass`, or a React Element of a derived `refClass`,
 * or a mounted DOM Node of `refClass` or a derived `refClass`.
 */
const isClassOrComponentFactory = (refClass) =>
    (inst) => isClassOrComponent(refClass, inst);

export {
    isExactClass,
    isDerivedClass,
    isInstanceOf,
    isExactComponent,
    isDerivedComponent,
    isClassOrComponent,
    isClassOrComponentFactory
};