【问题标题】:Where in the ESLint code is the rule matcher implemented?规则匹配器在 ESLint 代码中的哪个位置实现?
【发布时间】:2021-12-27 12:00:00
【问题描述】:

翻遍ESLint code,我找不到这种东西是在哪里实现的:

':function': (node: TSESTree.Node) => {
  onEnterFunction(node as TSESTree.FunctionLike);
},
':function:exit'(node: TSESTree.Node) {
  onLeaveFunction(node as TSESTree.FunctionLike);
},

'*'(node: TSESTree.Node) {
  if (nestingNodes.has(node)) {
    nesting++;
  }
},
'*:exit'(node: TSESTree.Node) {
  if (nestingNodes.has(node)) {
    nesting--;
    nestingNodes.delete(node);
  }
},

特别是像*:exit 这样的“匹配器”键,我见过更复杂的CSS-selector-like expressions。这些实现的处理程序在 ESLint 源代码中的什么位置?

我想看看它是如何实现的。

【问题讨论】:

    标签: algorithm css-selectors eslint


    【解决方案1】:

    貌似是在esquery中实现的。

    function matches(node, selector, ancestry, options) {
        if (!selector) { return true; }
        if (!node) { return false; }
        if (!ancestry) { ancestry = []; }
    
        switch(selector.type) {
            case 'wildcard':
                return true;
    
            case 'identifier':
                return selector.value.toLowerCase() === node.type.toLowerCase();
    
            case 'field': {
                const path = selector.name.split('.');
                const ancestor = ancestry[path.length - 1];
                return inPath(node, ancestor, path);
    
            }
            case 'matches':
                for (const sel of selector.selectors) {
                    if (matches(node, sel, ancestry, options)) { return true; }
                }
                return false;
    
            case 'compound':
                for (const sel of selector.selectors) {
                    if (!matches(node, sel, ancestry, options)) { return false; }
                }
                return true;
    
            case 'not':
                for (const sel of selector.selectors) {
                    if (matches(node, sel, ancestry, options)) { return false; }
                }
                return true;
    
            case 'has': {
                const collector = [];
                for (const sel of selector.selectors) {
                    const a = [];
                    estraverse.traverse(node, {
                        enter (node, parent) {
                            if (parent != null) { a.unshift(parent); }
                            if (matches(node, sel, a, options)) {
                                collector.push(node);
                            }
                        },
                        leave () { a.shift(); },
                        keys: options && options.visitorKeys,
                        fallback: options && options.fallback || 'iteration'
                    });
                }
                return collector.length !== 0;
    
            }
            case 'child':
                if (matches(node, selector.right, ancestry, options)) {
                    return matches(ancestry[0], selector.left, ancestry.slice(1), options);
                }
                return false;
    
            case 'descendant':
                if (matches(node, selector.right, ancestry, options)) {
                    for (let i = 0, l = ancestry.length; i < l; ++i) {
                        if (matches(ancestry[i], selector.left, ancestry.slice(i + 1), options)) {
                            return true;
                        }
                    }
                }
                return false;
    
            case 'attribute': {
                const p = getPath(node, selector.name);
                switch (selector.operator) {
                    case void 0:
                        return p != null;
                    case '=':
                        switch (selector.value.type) {
                            case 'regexp': return typeof p === 'string' && selector.value.value.test(p);
                            case 'literal': return `${selector.value.value}` === `${p}`;
                            case 'type': return selector.value.value === typeof p;
                        }
                        throw new Error(`Unknown selector value type: ${selector.value.type}`);
                    case '!=':
                        switch (selector.value.type) {
                            case 'regexp': return !selector.value.value.test(p);
                            case 'literal': return `${selector.value.value}` !== `${p}`;
                            case 'type': return selector.value.value !== typeof p;
                        }
                        throw new Error(`Unknown selector value type: ${selector.value.type}`);
                    case '<=': return p <= selector.value.value;
                    case '<': return p < selector.value.value;
                    case '>': return p > selector.value.value;
                    case '>=': return p >= selector.value.value;
                }
                throw new Error(`Unknown operator: ${selector.operator}`);
            }
            case 'sibling':
                return matches(node, selector.right, ancestry, options) &&
                    sibling(node, selector.left, ancestry, LEFT_SIDE, options) ||
                    selector.left.subject &&
                    matches(node, selector.left, ancestry, options) &&
                    sibling(node, selector.right, ancestry, RIGHT_SIDE, options);
            case 'adjacent':
                return matches(node, selector.right, ancestry, options) &&
                    adjacent(node, selector.left, ancestry, LEFT_SIDE, options) ||
                    selector.right.subject &&
                    matches(node, selector.left, ancestry, options) &&
                    adjacent(node, selector.right, ancestry, RIGHT_SIDE, options);
    
            case 'nth-child':
                return matches(node, selector.right, ancestry, options) &&
                    nthChild(node, ancestry, function () {
                        return selector.index.value - 1;
                    }, options);
    
            case 'nth-last-child':
                return matches(node, selector.right, ancestry, options) &&
                    nthChild(node, ancestry, function (length) {
                        return length - selector.index.value;
                    }, options);
    
            case 'class':
                switch(selector.name.toLowerCase()){
                    case 'statement':
                        if(node.type.slice(-9) === 'Statement') return true;
                        // fallthrough: interface Declaration <: Statement { }
                    case 'declaration':
                        return node.type.slice(-11) === 'Declaration';
                    case 'pattern':
                        if(node.type.slice(-7) === 'Pattern') return true;
                        // fallthrough: interface Expression <: Node, Pattern { }
                    case 'expression':
                        return node.type.slice(-10) === 'Expression' ||
                            node.type.slice(-7) === 'Literal' ||
                            (
                                node.type === 'Identifier' &&
                                (ancestry.length === 0 || ancestry[0].type !== 'MetaProperty')
                            ) ||
                            node.type === 'MetaProperty';
                    case 'function':
                        return node.type === 'FunctionDeclaration' ||
                            node.type === 'FunctionExpression' ||
                            node.type === 'ArrowFunctionExpression';
                }
                throw new Error(`Unknown class name: ${selector.name}`);
        }
    
        throw new Error(`Unknown selector type: ${selector.type}`);
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-13
      • 2016-11-22
      • 2017-08-11
      • 2017-12-04
      • 2020-10-26
      • 2019-11-10
      • 2017-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多