condition.js

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IfAllCondition = exports.IfOnlyExistCondition = exports.UnlessExistCondition = exports.IfExistCondition = void 0;
const typy_1 = require("typy");
const node_query_1 = __importDefault(require("@synvert-hq/node-query"));
const utils_1 = require("./utils");
/**
 * Condition is used to check if the node matches, condition won’t change the node scope.
 */
class Condition {
    /**
     * Create a Condition
     * @param {Instance} instance
     * @param {string|Object} nqlOrRules - nql or rules to find nodes
     * @param {ConditionOptions} options - to do find in specific child node, e.g. `{ in: 'callee' }`
     * @param {Function} func - a function to be called if nql or rules are matched.
     * @param {Function} elseFunc - a function to be called if nql or rules are not matched.
     */
    constructor(instance, nqlOrRules, options, func, elseFunc) {
        this.instance = instance;
        this.nodeQuery = new node_query_1.default(nqlOrRules, { adapter: instance.parser });
        this.options = options;
        this.func = func;
        this.elseFunc = elseFunc;
    }
    /**
     * If condition matches, run the func.
     */
    processSync() {
        if (this.match()) {
            this.func.call(this.instance, this.instance);
        }
        else if (this.elseFunc) {
            this.elseFunc.call(this.instance, this.instance);
        }
    }
    process() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.match()) {
                yield this.func.call(this.instance, this.instance);
            }
            else if (this.elseFunc) {
                yield this.elseFunc.call(this.instance, this.instance);
            }
        });
    }
    /**
     * Get the target node to process.
     * If `option.in` exists, the target node depends on `options.in`,
     * otherwise the target node is the current node.
     * e.g. source code of the node is `$.ajax({})` and `options` is `{ in: 'callee' }`
     * the target node is `$.ajax`.
     * @private
     * @returns {T}
     */
    targetNode() {
        if (this.options.in) {
            return (0, typy_1.t)(this.instance.currentNode, this.options.in).safeObject;
        }
        return this.instance.currentNode;
    }
}
/**
 * IfExistCondition checks if matching node exists in the node children.
 * @extends Condition
 */
class IfExistCondition extends Condition {
    /**
     * Check if any child node matches.
     */
    match() {
        const targetNode = this.targetNode();
        if (!targetNode)
            return false;
        const matchingNodes = this.nodeQuery.queryNodes(targetNode, {
            includingSelf: false,
            stopAtFirstMatch: true,
        });
        return matchingNodes.length > 0;
    }
}
exports.IfExistCondition = IfExistCondition;
/**
 * UnlessExistCondition checks if matching node doesn't exist in the node children.
 * @extends Condition
 */
class UnlessExistCondition extends Condition {
    /**
     * Check if none of child node matches.
     */
    match() {
        const targetNode = this.targetNode();
        if (!targetNode)
            return true;
        const matchingNodes = this.nodeQuery.queryNodes(targetNode, {
            includingSelf: false,
            stopAtFirstMatch: true,
        });
        return matchingNodes.length === 0;
    }
}
exports.UnlessExistCondition = UnlessExistCondition;
/**
 * IfOnlyExistCondition checks if node has only one child node and the child node matches.
 * @extends Condition
 */
class IfOnlyExistCondition extends Condition {
    /**
     * Check if only have one child node and the child node matches.
     */
    match() {
        const targetNode = this.targetNode();
        if (!targetNode)
            return false;
        const body = (0, utils_1.arrayBody)(targetNode, this.nodeQuery.adapter);
        return body.length === 1 && this.nodeQuery.matchNode(body[0]);
    }
}
exports.IfOnlyExistCondition = IfOnlyExistCondition;
/**
 * IfAllCondition checks if all matching nodes match options.match.
 * @extends Condition
 */
class IfAllCondition extends Condition {
    /**
     * Find all matching nodes, if all match options.match, run the func, else run the elseFunc.
     */
    match() {
        const targetNode = this.targetNode();
        if (!targetNode)
            return false;
        const nodes = this.nodeQuery.queryNodes(targetNode, {
            includingSelf: false,
        });
        if (nodes.length === 0) {
            return false;
        }
        return nodes.every(this._matchFunc.bind(this));
    }
    /**
     * Function to match node.
     * @private
     * @param {T} node
     * @returns {boolean} true if node matches
     */
    _matchFunc(node) {
        if (typeof this.options.match === "function") {
            return this.options.match(node);
        }
        else {
            return new node_query_1.default(this.options.match, {
                adapter: this.instance.parser,
            }).matchNode(node);
        }
    }
}
exports.IfAllCondition = IfAllCondition;