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

94.44% Statements 17/18
93.75% Branches 15/16
100% Functions 0/0
94.44% Lines 17/18
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            1x 1x 1x                                     1x           35x 35x   35x 24x   24x     11x                   61x 61x       61x                       13x   13x 2x   11x        
import * as ESTree from 'estree';
 
import { TNodeWithBlockStatement } from '../types/TNodeWithBlockStatement';
 
import { IBlockScopeTraceData } from '../interfaces/IBlockScopeTraceData';
 
import { ASTTreeBlockScopeAnalyzer } from './ASTTreeBlockScopeAnalyzer';
import { NodeUtils } from '../NodeUtils';
import { Utils } from '../Utils';
 
/**
 * This class appends node into a first deepest BlockStatement in order of function calls
 *
 * For example:
 *
 * function Foo () {
 *     var baz = function () {
 *
 *     }
 *
 *     baz();
 * }
 *
 * foo();
 *
 * Appends node into block statement of `baz` function expression.
 */
export class HiddenNodeAppender {
    /**
     * @param blockScopeBody
     * @param node
     * @param index
     */
    public static appendNode (blockScopeBody: ESTree.Node[], node: ESTree.Node, index: number = 0): void {
        const blockScopeTraceData: IBlockScopeTraceData[] = new ASTTreeBlockScopeAnalyzer<IBlockScopeTraceData>(blockScopeBody).analyze();
 
        if (!blockScopeTraceData.length) {
            NodeUtils.prependNode(blockScopeBody, node);
 
            return;
        }
 
        NodeUtils.prependNode(
            HiddenNodeAppender.getOptimalBlockScope(blockScopeTraceData, index).body,
            node
        );
    }
 
    /**
     * @param blockStatementBodyLength
     * @param threshold
     */
    public static getIndexByThreshold (blockStatementBodyLength: number, threshold: number = 0.1): number {
        Iif (threshold < 0 || threshold > 1) {
            throw new RangeError('`threshold` parameter should has value between 0 and 1');
        }
 
        return Utils.getRandomGenerator().integer({
            min: 0,
            max: Math.round(blockStatementBodyLength * threshold)
        });
    }
 
    /**
     * @param blockScopeTraceData
     * @param index
     * @returns {TNodeWithBlockStatement}
     */
    private static getOptimalBlockScope (blockScopeTraceData: IBlockScopeTraceData[], index: number): TNodeWithBlockStatement {
        const firstCall: IBlockScopeTraceData = blockScopeTraceData[index];
 
        if (firstCall.trace.length) {
            return HiddenNodeAppender.getOptimalBlockScope(firstCall.trace, 0);
        } else {
            return firstCall.callee;
        }
    }
}