diff --git a/lib/linter/node-event-generator.js b/lib/linter/node-event-generator.js index d56bef2fa9d..7e940173b3c 100644 --- a/lib/linter/node-event-generator.js +++ b/lib/linter/node-event-generator.js @@ -185,12 +185,24 @@ function compareSpecificity(selectorA, selectorB) { /** * Parses a raw selector string, and throws a useful error if parsing fails. * @param {string} rawSelector A raw AST selector + * @param {Set} knownTypes The known node types * @returns {Object} An object (from esquery) describing the matching behavior of this selector * @throws {Error} An error if the selector is invalid */ -function tryParseSelector(rawSelector) { +function tryParseSelector(rawSelector, knownTypes) { + + const rawSelectorWithoutExit = rawSelector.replace(/:exit$/u, ""); + + // no need to parse known types + if (knownTypes.has(rawSelectorWithoutExit)) { + return { + type: "identifier", + value: rawSelectorWithoutExit + }; + } + try { - return esquery.parse(rawSelector.replace(/:exit$/u, "")); + return esquery.parse(rawSelectorWithoutExit); } catch (err) { if (err.location && err.location.start && typeof err.location.start.offset === "number") { throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}`); @@ -204,14 +216,16 @@ const selectorCache = new Map(); /** * Parses a raw selector string, and returns the parsed selector along with specificity and type information. * @param {string} rawSelector A raw AST selector + * @param {Set} knownTypes The known node types that don't require parsing. * @returns {ASTSelector} A selector descriptor */ -function parseSelector(rawSelector) { +function parseSelector(rawSelector, knownTypes) { if (selectorCache.has(rawSelector)) { return selectorCache.get(rawSelector); } - const parsedSelector = tryParseSelector(rawSelector); + // no need to parse anything that matches node.type + const parsedSelector = tryParseSelector(rawSelector, knownTypes); const result = { rawSelector, @@ -260,9 +274,10 @@ class NodeEventGenerator { this.exitSelectorsByNodeType = new Map(); this.anyTypeEnterSelectors = []; this.anyTypeExitSelectors = []; + this.knownTypes = new Set(Object.keys(esqueryOptions.visitorKeys)); emitter.eventNames().forEach(rawSelector => { - const selector = parseSelector(rawSelector); + const selector = parseSelector(rawSelector, this.knownTypes); if (selector.listenerTypes) { const typeMap = selector.isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType; @@ -293,6 +308,12 @@ class NodeEventGenerator { * @returns {void} */ applySelector(node, selector) { + + if (this.knownTypes.has(selector.rawSelector)) { + this.emitter.emit(selector.rawSelector, node); + return; + } + if (esquery.matches(node, selector.parsedSelector, this.currentAncestry, this.esqueryOptions)) { this.emitter.emit(selector.rawSelector, node); }