CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-traverse

Traverse and transform objects by visiting every node on a recursive walk

Pending
Overview
Eval results
Files

context.mddocs/

Traversal Context

When using callback functions with traversal methods (forEach, map, reduce), the this context provides extensive information about the current node and methods for modifying the traversal behavior.

Context Properties

The callback's this object contains the following properties that describe the current traversal state:

Node Information

// Current node being visited
this.node      // Current node value
this.node_     // Original node value (before modifications)

// Path information  
this.path      // Array of keys from root to current node
this.key       // Property key of current node in its parent (undefined for root)

// Hierarchy information
this.parent      // Parent context object (undefined for root)  
this.parents     // Array of all parent contexts
this.keys        // Array of enumerable keys of current node (null for non-objects)
this.removedKeys // Object tracking keys removed from parent during traversal

State Flags

// Position flags
this.isRoot    // boolean - Whether current node is the root
this.notRoot   // boolean - Whether current node is not the root
this.isLeaf    // boolean - Whether current node has no children
this.notLeaf   // boolean - Whether current node has children
this.level     // number - Depth of current node (0 for root)

// Traversal state
this.circular  // Parent context if node creates circular reference, null otherwise
this.isFirst   // boolean - Whether this is first child of parent (set during traversal)
this.isLast    // boolean - Whether this is last child of parent (set during traversal)

Context Methods

The context provides methods for modifying nodes and controlling traversal flow:

Node Modification

/**
 * Update the current node value
 * @param {*} value - New value for the node
 * @param {boolean} [stopHere=false] - Whether to stop traversing this branch
 */
this.update(value, stopHere)

/**
 * Remove current node (splice from arrays, delete from objects)
 * @param {boolean} [stopHere=false] - Whether to stop traversing this branch
 */
this.remove(stopHere)

/**
 * Delete current node (uses delete operator even on arrays)
 * @param {boolean} [stopHere=false] - Whether to stop traversing this branch  
 */
this.delete(stopHere)

Traversal Control

/**
 * Register function to call before traversing children of current node
 * @param {Function} fn - Function to call before children
 */
this.before(fn)

/**
 * Register function to call after traversing children of current node
 * @param {Function} fn - Function to call after children
 */
this.after(fn)

/**
 * Register function to call before each child of current node
 * @param {Function} fn - Function to call before each child
 */
this.pre(fn)

/**
 * Register function to call after each child of current node
 * @param {Function} fn - Function to call after each child
 */  
this.post(fn)

/**
 * Stop the entire traversal immediately
 */
this.stop()

/**
 * Skip traversing children of the current node
 */
this.block()

Usage Examples

Basic Context Usage

const traverse = require('traverse');

const obj = { a: { b: { c: 1 } }, d: [2, 3] };

traverse(obj).forEach(function (x) {
    console.log('Path:', this.path);
    console.log('Key:', this.key);
    console.log('Value:', this.node);
    console.log('Is leaf:', this.isLeaf);
    console.log('Level:', this.level);
    console.log('---');
});

// Output shows path and context info for each node:
// Path: []          Key: undefined    Value: {a: {...}, d: [...]}    Is leaf: false    Level: 0
// Path: ['a']       Key: 'a'          Value: {b: {...}}              Is leaf: false    Level: 1  
// Path: ['a','b']   Key: 'b'          Value: {c: 1}                  Is leaf: false    Level: 2
// etc...

Node Modification

const traverse = require('traverse');

// Transform values based on context
const obj = { 
    numbers: [1, 2, 3],
    nested: { value: 4 }
};

traverse(obj).forEach(function (x) {
    if (typeof x === 'number') {
        // Double numbers at even levels, triple at odd levels
        const multiplier = this.level % 2 === 0 ? 2 : 3;
        this.update(x * multiplier);
    }
});

Conditional Removal

const traverse = require('traverse');

const obj = {
    items: [1, null, 2, undefined, 3, '', 4],
    nested: { a: null, b: 5, c: undefined }
};

// Remove falsy values
traverse(obj).forEach(function (x) {
    if (!x && x !== 0 && x !== false) {
        this.remove();
    }
});

Array vs Object Deletion

const traverse = require('traverse');

const obj = {
    arr: ['a', 'remove', 'b'],
    obj: { keep: 1, remove: 2, keep2: 3 }
};

traverse(obj).forEach(function (x) {
    if (x === 'remove' || x === 2) {
        if (Array.isArray(this.parent.node)) {
            this.remove(); // Splice from array
        } else {
            this.delete(); // Delete from object
        }
    }
});

Traversal Control

const traverse = require('traverse');

const obj = {
    public: { data: 1, more: 2 },
    private: { secret: 'hidden', confidential: 'data' },
    normal: 'value'
};

// Skip traversing private sections
traverse(obj).forEach(function (x) {
    if (this.key === 'private') {
        console.log('Skipping private section');
        this.block(); // Don't traverse children
    } else {
        console.log('Processing:', this.path, this.node);
    }
});

Before/After Hooks

const traverse = require('traverse');

const obj = { a: { b: [1, 2] }, c: 3 };

traverse(obj).forEach(function (x) {
    if (typeof x === 'object' && x !== null) {
        this.before(function (node) {
            console.log('About to traverse:', this.path);
        });
        
        this.after(function (node) {
            console.log('Finished traversing:', this.path);
        });
    }
});

Circular Reference Detection

const traverse = require('traverse');

const obj = { name: 'parent' };
obj.self = obj; // Create circular reference

traverse(obj).forEach(function (x) {
    if (this.circular) {
        console.log('Found circular reference at path:', this.path);
        console.log('Points back to path:', this.circular.path);
        // this.circular is the parent context that creates the cycle
    }
});

Path-Based Operations

const traverse = require('traverse');

const config = {
    database: { host: 'localhost', port: 5432 },
    cache: { host: 'redis-server', port: 6379 },
    api: { version: 'v1', timeout: 5000 }
};

// Update all host values to production hosts
traverse(config).forEach(function (x) {
    if (this.key === 'host' && this.path.length === 2) {
        const service = this.path[0]; // 'database', 'cache', etc.
        this.update(`prod-${service}.example.com`);
    }
});

Advanced Context Usage

Custom Property Ordering

const traverse = require('traverse');

const obj = { c: 1, a: 2, b: 3 };

traverse(obj).forEach(function (x) {
    if (typeof x === 'object' && x !== null) {
        this.before(function () {
            // Sort keys alphabetically before traversing
            if (this.keys) {
                this.keys.sort();
            }
        });
    }
});

Conditional Stopping

const traverse = require('traverse');

let found = false;
const obj = { 
    level1: { 
        level2: { 
            target: 'FOUND', 
            other: 'data' 
        } 
    } 
};

traverse(obj).forEach(function (x) {
    if (x === 'FOUND') {
        console.log('Found target at:', this.path);
        found = true;
        this.stop(); // Stop entire traversal
    }
});

Install with Tessl CLI

npx tessl i tessl/npm-traverse

docs

context.md

extraction.md

index.md

paths.md

traversal.md

tile.json