Repository navigation
perf(cli): give the Vitest resolver a resolveId hook filter - #3434
Merged
fengmk2 merged 7 commits intoNov 12, 2026
Merged
Conversation
fengmk2
approved these changes
Nov 12, 2026
fengmk2
left a comment
Member
There was a problem hiding this comment.
@jasonkuhrt Thanks for the contribution!
Before I merge, please fix the typecheck error first. 🤟
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
vite-plus:vitest-resolvernow declares aresolveIdhook filter, so its handler is called only forvitest,vitest/*,@vitest/*andvite-plus/test. In a Rolldown build every other import is rejected before the call into JavaScript. Refs #2307.Today, on v1.6.0
defineConfiginjects the plugin into every config, and itsresolveIdis a plain function, so a build calls it once per import to returnnull. Each call crosses from Rust into JavaScript and back.The same build through the published
defineConfigand through this branch's, alternating in one process, on a generated graph where each module imports the next two and nothing imports Vitest. Wall time ofbuild(), nine runs each on one machine (script below):resolveIdcallsThat is about 9 to 14 µs per skipped call on this graph. The graph is all resolution and no real code, so it shows the cost of a call, not what an app saves. #2307's own A/B on an app with 12,000 calls found no clear difference, which fits a saving of one or two tenths of a second.
The change
The handler body is unchanged. It moves under
handlerand gains:The filter is a superset of what the handler acts on:
isVitestFamilySpecifieracceptsvitestand the@vitest/*packages inVITEST_RESOLVER_PACKAGES, and thevite-plus/testbranch matches that exact id. The allowlist still decides which packages are redirected, and a new test checks every member of it against the filter, so an entry added outside those prefixes fails a test.In the dev server and in Vitest, Vite's plugin container applies the same filter in JavaScript before it calls the handler.
Tests
define-config-vitest-resolver.spec.tsgains:vitest, avitest/subpath,@vitest/mocker,vite-plus/test) and one for an unrelated import (react), where the emitted external import shows which resolver answered. Thereactcase shows the handler no longer runs for an unrelated import: its stub would answer if it were entered;VITEST_RESOLVER_PACKAGESmember, with and without a subpath, against the filter.The helpers that call the hook directly read
.handler.I ran the four
define-config-*suites (99 tests) on this source against the published 1.6.0 native binding, plusvp fmtand oxlint without its type-aware rules on the two files. I did not runvp checkor the fullpnpm test:unitlocally.Not claimed
This does not settle #2307: the plugin is still injected, and only its per-import call goes.
[PLUGIN_TIMINGS]can't name the resolver on v1.6.0, with or without the filter: Rolldown 1.2.18 times each callback inside JavaScript and lists one only from one second (source), and #2307 measured the resolver's at 2.78 ms.The benchmark script
Run from a folder two levels below the root of an installed checkout, in a folder with
vite-plus@1.6.0installed, on a Node that strips types, withN=6000orN=20000.The two
pnpm tsgoerrors in the resolver spec are fixed:findPluginreturnsPlugin | undefined, so the two casts are gone.