All files / src/hidden-node-appender StackTraceAnalyzer.ts

96.97% Statements 32/33
95.24% Branches 20/21
100% Functions 4/4
96.77% Lines 30/31
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 1461x             1x 1x                                                                   1x                 40x           40x             40x 20x   79x 7x   7x         20x     40x               245x 245x   3345x         56x         56x       56x           56x   56x               56x   56x   1324x 44x   44x     1280x           12x   12x         56x      
import * as estraverse from 'estraverse';
import * as ESTree from 'estree';
 
import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
 
import { IBlockScopeTraceData } from '../interfaces/IBlockScopeTraceData';
 
import { Nodes } from '../Nodes';
import { NodeUtils } from '../NodeUtils';
import { IAnalyzer } from '../interfaces/IAnalyzer';
 
/**
 * This class generates a structure with order of function trace calls
 *
 * For example:
 *
 * function Foo () {
 *     var baz = function () {
 *
 *     }
 *
 *     baz();
 * }
 *
 * foo();
 *
 * Will generate a structure like:
 *
 * [
 *      {
 *          callee: FOO_FUNCTION_NODE
 *          name: 'Foo',
 *          trace: [
 *              {
 *                  callee: BAZ_FUNCTION_NODE,
 *                  name: 'baz,
 *                  trace: []
 *              }
 *          ]
 *      }
 * ]
 */
export class ASTTreeBlockScopeAnalyzer <T> implements IAnalyzer<T> {
    /**
     * @type {ESTree.Node[]}
     */
    private blockScopeBody: ESTree.Node[];
 
    /**
     * @type {T[]}
     */
    private blockScopeTraceData: T[] = [];
 
    /**
     * @param blockScopeBody
     */
    constructor (blockScopeBody: ESTree.Node[]) {
        this.blockScopeBody = blockScopeBody;
    }
 
    /**
     * @returns {T}
     */
    public analyze (): T[] {
        if (this.blockScopeBody.length === 1) {
            estraverse.traverse(this.blockScopeBody[0], {
                enter: (node: ESTree.Node): any => {
                    if (Nodes.isBlockStatementNode(node)) {
                        this.analyzeRecursive(node.body, this.blockScopeTraceData);
 
                        return estraverse.VisitorOption.Skip;
                    }
                }
            });
        } else {
            this.analyzeRecursive(this.blockScopeBody, this.blockScopeTraceData);
        }
 
        return this.blockScopeTraceData;
    }
 
    /**
     * @param blockScopeBody
     * @param dataTree
     */
    private analyzeRecursive (blockScopeBody: ESTree.Node[], dataTree: any): void {
        for (let rootNode of blockScopeBody) {
            estraverse.traverse(rootNode, {
                enter: (node: ESTree.Node): any => {
                    if (
                        Nodes.isCallExpressionNode(node) &&
                        Nodes.isIdentifierNode(node.callee) &&
                        rootNode.parentNode === NodeUtils.getBlockScopeOfNode(node)
                    ) {
                        const calleeNode: TNodeWithBlockStatement|null = this.getCalleeBlockStatement(
                            NodeUtils.getBlockScopeOfNode(blockScopeBody[0]),
                            node.callee.name
                        );
 
                        Iif (!calleeNode) {
                            return estraverse.VisitorOption.Break;
                        }
 
                        const data: IBlockScopeTraceData = {
                            callee: calleeNode,
                            name: node.callee.name,
                            trace: []
                        };
 
                        dataTree.push(data);
 
                        this.analyzeRecursive(calleeNode.body, data.trace);
                    }
                }
            });
        }
    }
 
    private getCalleeBlockStatement (node: ESTree.Node, name: string): TNodeWithBlockStatement|null {
        let calleeBlockStatement: TNodeWithBlockStatement|null = null;
 
        estraverse.traverse(node, {
            enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                if (Nodes.isFunctionDeclarationNode(node) && node.id.name === name) {
                    calleeBlockStatement = node.body;
 
                    return estraverse.VisitorOption.Break;
                }
 
                if (
                    Nodes.isFunctionExpressionNode(node) &&
                    Nodes.isVariableDeclaratorNode(parentNode) &&
                    Nodes.isIdentifierNode(parentNode.id) &&
                    parentNode.id.name === name
                ) {
                    calleeBlockStatement = node.body;
 
                    return estraverse.VisitorOption.Break;
                }
            }
        });
 
        return calleeBlockStatement;
    }
}