Skip to content

Commit

Permalink
perf: Fast path for NodeEventGenerator
Browse files Browse the repository at this point in the history
Refs #16962
  • Loading branch information
nzakas committed Mar 24, 2023
1 parent 10022b1 commit 99ae371
Showing 1 changed file with 26 additions and 5 deletions.
31 changes: 26 additions & 5 deletions lib/linter/node-event-generator.js
Expand Up @@ -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<string>} 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}`);
Expand All @@ -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<string>} 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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 99ae371

Please sign in to comment.