Skip to content

perf: Use Map instead of AstMetadataApiWithTargetsResolver[] for faster matching#679

Merged
amilajack merged 3 commits intoamilajack:mainfrom
igeligel:performance-improvement-replace-rules-find-with-map
Apr 27, 2026
Merged

perf: Use Map instead of AstMetadataApiWithTargetsResolver[] for faster matching#679
amilajack merged 3 commits intoamilajack:mainfrom
igeligel:performance-improvement-replace-rules-find-with-map

Conversation

@igeligel
Copy link
Copy Markdown
Contributor

This refactors the getRulesForTargets function to return Map<string, AstMetadataApiWithTargetsResolver> for each expression matching.

Before it returned:

{
  CallExpression: AstMetadataApiWithTargetsResolver[];
  NewExpression: AstMetadataApiWithTargetsResolver[];
  MemberExpression: AstMetadataApiWithTargetsResolver[];
  ExpressionStatement: AstMetadataApiWithTargetsResolver[];
  Literal: AstMetadataApiWithTargetsResolver[];
}

Now it returns:

{
  callExpression: RuleMap;
  newExpression: RuleMap;
  expressionStatement: RuleMap;
  memberExpression: RuleMap;
  literal: RuleMap;
};

This also requires changes now in helpers.tsx and makes a lot of rules.find( calls much faster. O(1) instead of O(n).

I benchmarked the changes as well (Thanks for providing the benchmarks):

Before: https://gist.github.com/igeligel/8c2dd38d1da1363f3a57c3820b208688#file-1-before-txt
After: https://gist.github.com/igeligel/8c2dd38d1da1363f3a57c3820b208688#file-2-after-txt

Repo Before mean (s) After mean (s) Change
bootstrap 0.609 0.335 ~45% faster
jquery 1.089 0.741 ~32% faster
handlebars.js 0.462 0.376 ~19% faster
aframe 8.070 6.280 ~22% faster
pixi.js 2.284 1.743 ~24% faster
create-react-app 2.685 2.202 ~18% faster
electron-react-boilerplate 0.0316 0.0271 ~14% faster
preact 0.00689 0.00528 ~23% faster

There are some more improvements as well that I will follow up in other Pull Requests.

@joshkel
Copy link
Copy Markdown

joshkel commented Feb 18, 2026

If I'm following this correctly, this looks like it serves the same purpose as my work at #615, and it looks simpler too. I'll close #615 if this is merged.

Use Maps instead of arrays
@igeligel igeligel force-pushed the performance-improvement-replace-rules-find-with-map branch from 3d3032d to ca74d10 Compare February 20, 2026 12:07
@igeligel
Copy link
Copy Markdown
Contributor Author

So updated the branch. Updated benchmarks: https://gist.github.com/igeligel/548048b85386d1f3760ba80e5be0f628

@igeligel
Copy link
Copy Markdown
Contributor Author

Fixed conflicts by merging the main function.

New benchmark results: https://gist.github.com/igeligel/fabb1ddfdeefcf66c777788eb875845f

cc @amilajack

@amilajack
Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@kevinpeters-stripe
Copy link
Copy Markdown

Hey @amilajack ,
Mind having a look at this again please?

@amilajack amilajack requested a review from Copilot April 27, 2026 14:36
@amilajack
Copy link
Copy Markdown
Owner

@codex review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors rule selection and lookup in the compat ESLint rule to speed up matching by replacing per-node linear searches over arrays with Map-based lookups.

Changes:

  • Refactor getRulesForTargets to build and return per-AST-node-type Map structures for faster rule retrieval.
  • Update helper lint functions (lintCallExpression, lintNewExpression, etc.) to consume Map inputs instead of arrays and use .get() lookups.
  • Rewire compat rule wiring to pass the new maps into the lint helpers.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/rules/compat.ts Builds per-node-type rule maps in getRulesForTargets and updates rule visitor bindings to pass maps.
src/helpers.ts Changes lint helper APIs to accept rule maps and perform Map lookups during AST traversal.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/rules/compat.ts
Comment on lines +150 to +154
const callExpression = new Map<string, AstMetadataApiWithTargetsResolver>();
for (const rule of byType.CallExpression) {
const key = rule.object.toLowerCase();
if (!callExpression.has(key)) callExpression.set(key, rule);
}
Comment thread src/rules/compat.ts Outdated
Comment on lines +172 to +175
const protoChainKey = rule.protoChainId.toLowerCase();
if (!memberExpression.has(protoChainKey))
memberExpression.set(protoChainKey, rule);
const key = (rule.property ? `${rule.object}.${rule.property}` : rule.object).toLowerCase();
Comment thread src/helpers.ts
Comment on lines 71 to 75
if (!node.callee) return;
const calleeName = node.callee.name;
const failingRule = rules.find((rule) => rule.object === calleeName);
if (!calleeName) return;
const failingRule = rulesMap.get(calleeName.toLowerCase());
if (failingRule)
Comment thread src/helpers.ts Outdated
const failingRule = rules.find(
(rule) => rule.protoChainId === protoChainId
);
const failingRule = rulesMap.get(protoChainId.toLowerCase());
Comment thread src/rules/compat.ts
Comment on lines 132 to +133
const getRulesForTargets = memoize(
(targetsJSON: string, lintAllEsApis: boolean): RulesFilteredByTargets => {
const result = {
(targetsJSON: string, lintAllEsApis: boolean): RuleMapsForTargets => {
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 16e3367912

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/helpers.ts Outdated
const calleeName = node.callee.name;
const failingRule = rules.find((rule) => rule.object === calleeName);
if (!calleeName) return;
const failingRule = rulesMap.get(calleeName.toLowerCase());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore case-sensitive matching for call/new/expression

Lowercasing identifiers during lookup changes CallExpression/NewExpression/ExpressionStatement matching from exact to case-insensitive, which introduces false positives for non-equivalent symbols (for example new url() now matches the URL rule). Previous logic only allowed case-insensitive matching in the browser-global member-expression path; this broad lookup makes unrelated identifiers fail compat checks purely because they share lowercase forms.

Useful? React with 👍 / 👎.

Comment thread src/helpers.ts Outdated
Comment on lines +210 to +211
rulesMap.get(`${objectName}.${propertyName}`.toLowerCase()) ??
rulesMap.get(objectName.toLowerCase());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve property-case checks in member lookup

The member-expression key is lowercased as ${objectName}.${propertyName}, so property matching becomes case-insensitive even for non-browser globals. Because the subsequent guard only validates failingRule.object case, Promise.Resolve will match Promise.resolve and be reported as unsupported. This regresses correctness by flagging APIs that are not actually referenced.

Useful? React with 👍 / 👎.

@igeligel
Copy link
Copy Markdown
Contributor Author

igeligel commented Apr 27, 2026

Thanks for the review! Will adjust based on the comments later.

@amilajack
Copy link
Copy Markdown
Owner

feel free to only focus on the codex comments

Feedback
@igeligel
Copy link
Copy Markdown
Contributor Author

@amilajack amilajack merged commit c101643 into amilajack:main Apr 27, 2026
5 checks passed
amilajack pushed a commit that referenced this pull request Apr 29, 2026
…faster matching (#679)

* perf: compat

Use Maps instead of arrays

* refactor: ci

Feedback
amilajack pushed a commit that referenced this pull request Apr 29, 2026
github-actions Bot pushed a commit that referenced this pull request Apr 29, 2026
## [7.0.2](v7.0.1...v7.0.2) (2026-04-29)

### Performance Improvements

* Use `Map` instead of `AstMetadataApiWithTargetsResolver[]` for faster matching ([#679](#679)) ([fecdcc6](fecdcc6))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants