Visitar URL original
fix: stop annotated destructuring from widening the local declaration scan by MFA-G · Pull Request #570 · unjs/unimport · GitHub
Skip to content

fix: stop annotated destructuring from widening the local declaration scan - #570

Open
MFA-G wants to merge 1 commit into
unjs:mainfrom
MFA-G:fix/typed-destructuring-exclude
Open

MFA-G wants to merge 1 commit into
unjs:mainfrom
MFA-G:fix/typed-destructuring-exclude

Conversation

@MFA-G

@MFA-G MFA-G commented Sep 23, 2026 •

Copy link
Copy Markdown

Problem

The regex scanner that collects locally declared names (RE_EXCLUDE[3]) only ends a declaration at =, ;, \n, of or in. With a TypeScript annotation between the pattern and the =, the destructuring branch can't end at its own bracket. Because of the s flag, the lazy \{.*?\} / \[.*?\] then keeps going across lines until some later }/] happens to come right before =, ; or a newline.

Every identifier in that span is treated as a local declaration, so it is never auto-imported:

const { value }: Props = props

const hooks = [
  useState,
  useEffect,
]

const config = { value }

On main this injects nothing: { value }: Props = props … const config = { value } is captured as one pattern, and useState / useEffect are counted as locals. Array patterns do the same (const [first]: [number] = f() stretches to the next ] followed by = or a newline). A plain annotation like const count: number = 1 also yields count: number rather than count.

This looks like a different trigger for the same widening described in #525. It is not a fix for the for-loop case there, which #522 already handled.

Fix

Match an optional same-line annotation (:[^=;\n]*) outside the captured group, before the terminator. Destructuring patterns now close at their own bracket, and the annotation text is no longer part of the collected names. The annotation can't cross =, ; or a newline, so it can't extend the match.

Tests

  • test/existing-scanning.test.ts: annotated simple, object and array declarations, plus two multi-line cases that check the scan stops at the annotated pattern. These 5 cases fail on main.
  • test/cases/positive/ts-annotated-destructuring.ts with its snapshot: the end-to-end repro above. It now gets import { useState, useEffect } from 'react'. On main it fails because nothing is injected.

vitest run: 28 files, 260 tests pass. eslint src test and vue-tsc --noEmit are clean.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of TypeScript type annotations in variable declarations, including destructuring. Variables are now identified correctly, and following array or object declarations are kept separate.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 87d75e8e-0801-4a8e-9928-3bdcc9d1de2e

📥 Commits

Reviewing files that changed from the base of the PR and between bbd0776 and e8181f5.

📒 Files selected for processing (4)
  • src/regexp.ts
  • test/cases/output/default/ts-annotated-destructuring.ts
  • test/cases/positive/ts-annotated-destructuring.ts
  • test/existing-scanning.test.ts

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The declaration exclusion regex now accepts optional TypeScript annotations before declaration delimiters. Regression tests and fixtures cover identifier extraction from annotated declarations and destructuring.

Changes

Annotated declaration matching

Layer / File(s) Summary
Match annotated declarations and verify extraction
src/regexp.ts, test/existing-scanning.test.ts, test/cases/positive/ts-annotated-destructuring.ts, test/cases/output/default/ts-annotated-destructuring.ts
The regex accepts optional type annotations while retaining destructuring captures. Tests and fixtures cover extracted identifiers and ensure later declarations remain separate.

Priority: ⚪ Not assessed

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to e8181

Annotated declarations retain correct local-variable detection, and later hook references remain eligible for auto-import. No actionable risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preventing TypeScript-annotated destructuring from widening the local declaration scan.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 4…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant