fix: handle for-of/for-in loop declarations in regex scanner by harlan-zw · Pull Request #522 · unjs/unimport · GitHub
Skip to content

fix: handle for-of/for-in loop declarations in regex scanner#522

Merged
antfu merged 2 commits intounjs:mainfrom
harlan-zw:fix/for-loop-destructuring-regex
Apr 13, 2026
Merged

fix: handle for-of/for-in loop declarations in regex scanner#522
antfu merged 2 commits intounjs:mainfrom
harlan-zw:fix/for-loop-destructuring-regex

Conversation

@harlan-zw
Copy link
Copy Markdown
Contributor

@harlan-zw harlan-zw commented Apr 10, 2026

🔗 Linked issue

Resolves #521
Resolves #487
Supersedes #488

❓ Type of change

  • 🐞 Bug fix

📚 Description

RE_EXCLUDE[3] only accepted =, ;, or \n as terminators after variable patterns. For for (const [a, b] of ...), the \[.*?\] branch matched [a, b] but then couldn't terminate on of. With the s (dotall) flag, .*? extended across newlines searching for another ] followed by a valid terminator, swallowing any variable declarations in between and causing duplicate import injection.

Added \bof\b and \bin\b as valid terminators so for-of and for-in loop variables are correctly excluded from auto-import candidates. Word boundaries prevent false matches inside identifiers (e.g. const offset = 5 still works correctly), which is an improvement over #488's approach that used bare of without boundaries.

All 197 tests pass (including 7 new ones), no snapshot changes.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of variable declarations in for...of and for...in loops, including destructuring patterns.
  • Tests

    • Added regression test for destructuring in loop declarations.
    • Extended test coverage for loop-based variable extraction scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.08%. Comparing base (a4a5e0a) to head (9e246f4).
⚠️ Report is 157 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #522      +/-   ##
==========================================
- Coverage   98.73%   95.08%   -3.66%     
==========================================
  Files          14       13       -1     
  Lines        1817      976     -841     
  Branches      374      322      -52     
==========================================
- Hits         1794      928     -866     
- Misses         23       45      +22     
- Partials        0        3       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/existing-scanning.test.ts (1)

12-18: Consider adding var loop cases to match the regex contract.

Nice expansion of coverage. Since Line 13 in src/regexp.ts explicitly supports var, adding for (var key in obj) {} and for (var [a, b] of items) {} would close the matrix and guard future regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/existing-scanning.test.ts` around lines 12 - 18, Add missing `var` loop
test cases to mirror the existing `let`/`const` coverage: update the test data
in existing-scanning.test.ts by adding entries for "for (var [a, b] of items)
{}" expecting ['a','b'] and "for (var key in obj) {}" expecting ['key']; this
aligns the tests with the regex behavior implemented in src/regexp.ts (the
support for `var` at the pattern near Line 13) and prevents regressions for
`var`-declared loop bindings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/existing-scanning.test.ts`:
- Around line 12-18: Add missing `var` loop test cases to mirror the existing
`let`/`const` coverage: update the test data in existing-scanning.test.ts by
adding entries for "for (var [a, b] of items) {}" expecting ['a','b'] and "for
(var key in obj) {}" expecting ['key']; this aligns the tests with the regex
behavior implemented in src/regexp.ts (the support for `var` at the pattern near
Line 13) and prevents regressions for `var`-declared loop bindings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5686255c-77e0-4537-8482-24e0851a14e8

📥 Commits

Reviewing files that changed from the base of the PR and between 3b0a712 and e1f926e.

📒 Files selected for processing (3)
  • src/regexp.ts
  • test/cases/negative/for-loop-destructuring.js
  • test/existing-scanning.test.ts

The `RE_EXCLUDE[3]` regex for detecting local variable declarations
only accepted `=`, `;`, or `\n` as terminators after the variable
pattern. This meant `for (const [a, b] of ...)` destructuring was
not recognized as a local declaration, because `of` did not satisfy
the terminator. With the `s` (dotall) flag, the `\[.*?\]` branch
could then extend across newlines and swallow subsequent `var`/`let`/
`const` declarations, causing unimport to inject duplicate imports.

Add `\bof\b` and `\bin\b` as valid terminators so for-of and for-in
loop variables are correctly excluded from auto-import candidates.

Closes unjs#521
@harlan-zw harlan-zw force-pushed the fix/for-loop-destructuring-regex branch from e1f926e to 1cb41b3 Compare April 10, 2026 05:43
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

Fixes a regex-based local-variable exclusion edge case so for (const/let/var … of …) and for (… in …) loop declarations are properly treated as local declarations during regex scanning, preventing unintended “swallowing” and subsequent incorrect auto-import injection.

Changes:

  • Extend RE_EXCLUDE[3] to accept of/in as valid terminators for loop declarations.
  • Add targeted unit tests for for-of/for-in declarations and a regression case ensuring subsequent declarations aren’t swallowed.
  • Add a negative fixture ensuring ref isn’t auto-imported when declared after a for-of destructuring loop.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/regexp.ts Updates the local-variable exclusion regex to terminate on of/in in addition to = ; \n.
test/existing-scanning.test.ts Adds unit coverage for for-of/for-in declarations and the swallowing regression.
test/cases/negative/for-loop-destructuring.js Adds an integration-style regression fixture to ensure no import injection occurs.

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

@harlan-zw harlan-zw requested a review from antfu April 10, 2026 05:56
@antfu antfu merged commit c6e84f6 into unjs:main Apr 13, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants