JSFiddle - React, Tailwind, and code Playground

Object traverser proxy

by Csaba Hellinger

HTML

<p>Check the console for results.</p>
<h3>Features:</h3>
<ul>
    <li>Guards against <tt>undefined</tt> anywhere in the expression.</li>
    <li>Keeps detailed debug information about where in the expression we got undefined, and what was the last valid value.</li>
    <li>Can inline lodash calls in the chain and keep the logical order of things.</li>
    <li>Log helper for debugging.</li>
</ul>

<h3>TODO:</h3>
<ul>
    <li>Include the complete lodash? With a decorator?</li>
    <li>Add map/filter/reduce methods.</li>
    <li>Method error handling. (Ex: calling map on a number -> $error)</li>
    <li>Change $value to a method. Auto log if <tt>undefined</tt>.</li>
    <li>Move to node, add test suite.</li>
    <li>Add $throw method?</li>
</ul>

CSS

tt { color: darkblue; }

JavaScript

// test data

const model = {
	channels: [
    	{
        	id: 'c1',
            name: 'HBO',
            programs: [
            	{
                	id: 'c1p1',
                    name: 'Game of Thrones',
                    isLive: false
                }
            ]
        }
    ]
};

// library code

class ValueNode {
	constructor(value, path = 'root', error) {
    	this.$value = value;
        this.$path = path;
        this.$error = error;
    }
    static getNextError(node, nextValue, nextPath) {
        return node.$error || (
            nextValue === undefined 
            ? {
                $path: nextPath,
                $lastPath: node.$path,
                $lastValue: node.$value
            }
            : undefined
        );    
    }
    $log() {
    	const value = this.$value;
    	console.group(this.$path, '=', value);
        if (value === undefined) {        	
            console.log('Undefined since:',this.$error.$path);
            console.log('Last defined value:', this.$error.$lastPath, '=', this.$error.$lastValue);
        }
        console.groupEnd('');
        console.log('');
        return this;
    }
    $_find(...params) {
    	const nextValue = _.find(this.$value, ...params),
        	nextPath = `${this.$path}.$_find(${params.map(JSON.stringify)})`,
            nextError = ValueNode.getNextError(this, nextValue, nextPath);
        return valueProxy(nextValue, nextPath, nextError);
    }
}

const valueProxy = (() => {
    const handler = {
        get (obj, prop, receiver) {
        	// is it a method?
            if (prop.startsWith('$')) {
            	return Reflect.get(obj, prop);
            }
            // are we an array/object?
            const nextValue = typeof receiver.$value === 'object' 
                    ? Reflect.get(receiver.$value, prop)
                    : undefined,
                nextPath = Array.isArray(receiver.$value)
                	? `${receiver.$path}[${prop}]`
                    :...