feat(vite): improved optimizeDeps hints#34320
Conversation
|
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds tests for the Vite logger and extends the logger to detect newly optimized dependency candidates, accumulate them into a pending set, and emit a single debounced suggestion (after 2500 ms) with a generated 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@packages/vite/src/utils/logger.ts`:
- Around line 76-78: The early return when hasShownSuggestion is true is
suppressing Vite's original log; remove that return and instead only skip the
custom hint/timer logic so the original log still flows through. Concretely,
inside the type === 'info' && msg.includes(...) branch in logger.ts, replace the
early "if (hasShownSuggestion) { return }" with a guarded block like "if
(!hasShownSuggestion) { /* setTimeout/hint logic and set hasShownSuggestion =
true */ }" so the native Vite message (msg) is still processed/emitted while the
hint is only shown once.
- Around line 15-17: The module-level suggestion state (suggestedDeps,
suggestionTimer, hasShownSuggestion) is shared across all createViteLogger
instances and never cleaned in production; move these variables into
createViteLogger so each logger has its own
suggestedDeps/suggestionTimer/hasShownSuggestion, add a dispose() (or cleanup)
method on the returned logger that clears suggestionTimer and empties
suggestedDeps/flags (or call resetSuggestionState from there), and update server
shutdown hooks to call logger.dispose() so timeouts are cleared and state
doesn't leak between servers or restarts.
- Around line 80-83: Extract the inline regex /\u001B\[[\d;]*m/g into a named
constant (e.g., ANSI_ESCAPE_REGEX) and replace the .replace call in the deps
parsing logic with .replace(ANSI_ESCAPE_REGEX, ''). Add a local Biome rule
suppression comment immediately above the constant to disable only
noControlCharactersInRegex for that declaration (for example using the
appropriate biome-disable-next-line comment), so the control-character regex is
isolated and the rule is suppressed only for that constant.
🧹 Nitpick comments (4)
packages/vite/src/utils/logger.ts (1)
93-107: The suggestion timer is never cancelled on server shutdown.If the Vite dev server is stopped (e.g. user restarts Nuxt or hits Ctrl+C) while the 2500 ms timer is pending, the callback fires after teardown and calls
logger.infoon a potentially stale context. This is a minor concern in practice (dev-only, process usually exits), but for correctness you could clear it via a Nuxtclosehook or expose a cleanup function.Additionally, consider whether the
returnon line 77 (if (hasShownSuggestion) { return }) should come before or after the dependency-parsing block. Currently, whenhasShownSuggestionis true, the entire log message (including Vite's original "new dependencies optimized" info) is suppressed. If the intent is only to suppress the hint, you'd want to move the early return to just before the timer scheduling (line 93) and let the original Vite message pass through.packages/vite/src/plugins/dev-server.ts (1)
151-156: Consider extracting the 3000 ms magic number and clearing the timer on server close.The 3-second delay is meaningful (waiting for the initial page load to settle) but unexplained. A named constant or brief comment would help future readers understand the rationale. Also, if the server is torn down within those 3 seconds, the timer fires on a stale context.
Suggested improvement
+ const FIRST_LOAD_SETTLE_MS = 3000 // Wait for initial page load to complete before silencing dep suggestions let firstRequest = true + let firstLoadTimer: ReturnType<typeof setTimeout> | undefined const viteMiddleware = defineEventHandler(async (event: H3V1Event | H3V2Event) => { if (firstRequest) { firstRequest = false - setTimeout(onFirstPageLoad, 3000) + firstLoadTimer = setTimeout(onFirstPageLoad, FIRST_LOAD_SETTLE_MS) }Then clear
firstLoadTimerin a Nuxtclosehook if one is available in this scope.packages/vite/src/utils/logger.test.ts (2)
25-29: AddafterEachto restore real timers.
vi.useFakeTimers()is called inbeforeEachbut real timers are never restored. While vitest may handle this between test files, it's best practice to pair it withvi.useRealTimers()inafterEachto avoid leaking fake timers into other tests in the same suite if the suite grows.Proposed fix
beforeEach(() => { vi.clearAllMocks() vi.useFakeTimers() resetSuggestionState() }) + + afterEach(() => { + vi.useRealTimers() + })Don't forget to add
afterEachto the import on line 1.
68-68: Brittle exact call-count assertion.
toHaveBeenCalledTimes(4)depends on knowing thatoutput()forwards each of the 3 info messages tologger.infoplus the 1 suggestion. If the internal forwarding logic changes (e.g. one of those messages gets suppressed or an extra log is added), this test breaks for the wrong reason. Consider asserting only that the suggestion message appeared exactly once, which is the behaviour under test.Proposed alternative
- expect(logger.info).toHaveBeenCalledTimes(4) // 3 original info logs + 1 suggestion + // Verify exactly one suggestion was emitted + const suggestionCalls = vi.mocked(logger.info).mock.calls.filter( + call => (call[0] as string).includes('Vite has discovered new dependencies'), + ) + expect(suggestionCalls).toHaveLength(1)
2ce98c7 to
cb9d70b
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/vite/src/utils/logger.test.ts`:
- Line 93: The test calls viteLogger.clearScreen() without the required
parameter; update the call to pass a valid vite.LogType (for example
viteLogger.clearScreen('info') or another appropriate LogType literal) so the
signature vite.Logger['clearScreen'] is satisfied; if necessary, import or
reference the vite.LogType union or provide a cast (e.g., 'info' as
vite.LogType) to keep types correct.
🧹 Nitpick comments (1)
packages/vite/src/utils/logger.test.ts (1)
25-28: Restore real timers after each test to avoid leaking fake timers.
vi.useFakeTimers()is called inbeforeEachbut never restored. This can affect other test suites in the same process.Proposed fix
beforeEach(() => { vi.clearAllMocks() vi.useFakeTimers() }) + + afterEach(() => { + vi.useRealTimers() + })You'll also need to add
afterEachto the import on line 1:-import { beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
58200f0 to
b8f9b6d
Compare
When Vite discovers new dependencies at runtime and reloads the page, show a one-time hint with the nuxt.config.ts snippet to pre-bundle them.
b8f9b6d to
afa60b2
Compare
@nuxt/kit
@nuxt/nitro-server
nuxt
@nuxt/rspack-builder
@nuxt/schema
@nuxt/vite-builder
@nuxt/webpack-builder
commit: |
Merging this PR will not alter performance
Comparing Footnotes
|
|
Thanks and sorry, some edits after coming back with fresh eyes. |
|
Oh i got one more idea 😆 |
|
Okay, sorry for the pivot, but I think this is ultimately more useful (same direction, just more fleshed out). Will think over it the next couple of days, as I'm not fully convinced (i.e maybe we should keep Vite logs), but please lmk if you have feedback. |
🔗 Linked issue
resolves #29381, resolves #28195
(Both of these may be considered fixed already with v3.12.0 but this PR may help close them off?)
📚 Description
When Vite discovers new dependencies that need bundling in development runtime, it triggers a full page reload, which can be annoying. If users want to stop this from happening, the pathway is unclear, especially if they don't know how Vite works.
This PR adds two improvements to the developer experience around
optimizeDeps:nuxt.config.tssnippet to pre-bundle them.optimizeDeps.includecontains entries that can't be resolved (typos, removed packages, module-added entries), we warn with a suggested config fix.Before
Vite's default logging is terse and doesn't tell the user what to do about it:
After
Clear, actionable hints with copy-pasteable config:
Unresolvable entries: warns at startup with a clean config snippet:
Runtime-discovered deps: shown once after Vite finishes discovering new deps: