Skip to content

fix(devtools): remove the correct module in uninstallNuxtModule - #1019

Merged
antfu merged 2 commits into
nuxt:mainfrom
antfubot:fix/003-uninstall-nuxt-module
Jul 14, 2026
Merged

fix(devtools): remove the correct module in uninstallNuxtModule#1019
antfu merged 2 commits into
nuxt:mainfrom
antfubot:fix/003-uninstall-nuxt-module

Conversation

@antfubot

Copy link
Copy Markdown
Collaborator

uninstallNuxtModule edited the user's nuxt.config using
config.modules.splice(index - 1, 1) inside a forEach over
Object.values(config.modules). This off-by-one meant the module the
user asked to remove was never deleted — a different, innocent module
was removed instead (for a target at index 0, splice(-1, 1) removes
the last module). The corrupted config was then written back to
disk, silently corrupting the user's Nuxt config on a headline
"remove module" feature.

This extracts a pure, unit-tested removeNuxtModuleFromCode(source, name)
helper (packages/devtools/src/utils/nuxt-config.ts) that filters the
modules array by value, correctly handling both string entries
('@nuxt/image') and tuple entries with options
(['@nuxt/image', { ... }]), and wires it into uninstallNuxtModule
in place of the manual splice.

  • packages/devtools/src/utils/nuxt-config.ts — new pure helper
  • packages/devtools/src/server-rpc/npm.tsuninstallNuxtModule now delegates to the helper
  • packages/devtools/test/remove-nuxt-module.test.ts — regression coverage for the splice bug, tuple entries, and the absent-module no-op

Created with the help of an AI agent.

uninstallNuxtModule used config.modules.splice(index - 1, 1) inside a
forEach, an off-by-one that never removed the requested module and
instead deleted a different, innocent one (splice(-1, 1) for a target
at index 0 removes the last module). The corrupted config was then
persisted to disk.

Extract a pure removeNuxtModuleFromCode(source, name) helper that
filters the modules array by value, correctly handling both string
entries and tuple entries with options, and wire it into
uninstallNuxtModule in place of the manual splice.

Closes plans/003.
@antfubot
antfubot force-pushed the fix/003-uninstall-nuxt-module branch from efa0a82 to 3414f1d Compare July 14, 2026 06:06
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad668895-fd82-4a81-8012-f094bc973387

📥 Commits

Reviewing files that changed from the base of the PR and between 3414f1d and 6580688.

📒 Files selected for processing (1)
  • packages/devtools/src/utils/nuxt-config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/devtools/src/utils/nuxt-config.ts

📝 Walkthrough

Walkthrough

The uninstall flow now delegates Nuxt config editing to removeNuxtModuleFromCode. The utility parses the config, removes matching string or tuple module entries, and returns regenerated code. New Vitest coverage verifies removal behavior, tuple options, missing modules, and first-entry handling. Plan 003 is marked as done.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: uninstallNuxtModule now removes the intended module.
Description check ✅ Passed The description matches the changeset and explains the off-by-one removal bug and the new helper/test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 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.

packages/devtools/src/utils/nuxt-config.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/devtools/test/remove-nuxt-module.test.ts (1)

13-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consolidate redundant tests.

This test duplicates the inputs and assertions of the preceding test (lines 7-11). Since the first test already acts as the regression guard for preserving @nuxt/content (the remaining module), you can safely remove this block or combine their descriptions to avoid redundant execution.

♻️ Proposed cleanup
-  it('removes the first module without dropping the last', () => {
-    const out = removeNuxtModuleFromCode(base, '`@nuxt/image`')
-    expect(out).toContain('`@nuxt/content`') // regression guard for the splice(-1) bug
-  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools/test/remove-nuxt-module.test.ts` around lines 13 - 16,
Remove the redundant test block around removeNuxtModuleFromCode, or merge its
description and assertion into the preceding test; retain a single regression
assertion verifying that removing `@nuxt/image` preserves `@nuxt/content`.
packages/devtools/src/server-rpc/npm.ts (1)

147-150: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

Synchronize file modifications and batch dev-server restarts for uninstalls.

The config file is read before the long-running npm process and written back after it completes. Because it currently bypasses the latestGenerated and installSet batching logic used by installNuxtModule, concurrent uninstalls (or overlapping installs/uninstalls) can cause a race condition (TOCTOU) overwriting each other's config changes, and will trigger multiple overlapping dev-server restarts.

Consider extracting a shared concurrency queue to manage latestGenerated across both install and uninstall operations so they safely sequence their config writes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools/src/server-rpc/npm.ts` around lines 147 - 150, Update the
uninstall flow around magicastGuard and removeNuxtModuleFromCode to use the same
shared concurrency queue and latestGenerated/installSet batching state as
installNuxtModule. Sequence config reads, transformations, and writes through
that shared mechanism so concurrent or overlapping install/uninstall operations
preserve each other’s changes and trigger only batched dev-server restarts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/devtools/src/utils/nuxt-config.ts`:
- Around line 16-22: Update the config.modules cleanup logic to mutate the
existing array in place instead of assigning the result of filter(). In the
surrounding module-removal logic, iterate entries in reverse and splice matching
string entries or array entries whose first element equals name, preserving all
nonmatching entries and their existing formatting/comments.

---

Nitpick comments:
In `@packages/devtools/src/server-rpc/npm.ts`:
- Around line 147-150: Update the uninstall flow around magicastGuard and
removeNuxtModuleFromCode to use the same shared concurrency queue and
latestGenerated/installSet batching state as installNuxtModule. Sequence config
reads, transformations, and writes through that shared mechanism so concurrent
or overlapping install/uninstall operations preserve each other’s changes and
trigger only batched dev-server restarts.

In `@packages/devtools/test/remove-nuxt-module.test.ts`:
- Around line 13-16: Remove the redundant test block around
removeNuxtModuleFromCode, or merge its description and assertion into the
preceding test; retain a single regression assertion verifying that removing
`@nuxt/image` preserves `@nuxt/content`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59541815-cc50-4fc1-a155-f1a316a00d9e

📥 Commits

Reviewing files that changed from the base of the PR and between f71b190 and efa0a82.

📒 Files selected for processing (5)
  • packages/devtools/src/server-rpc/npm.ts
  • packages/devtools/src/utils/nuxt-config.ts
  • packages/devtools/test/remove-nuxt-module.test.ts
  • plans/003-fix-uninstall-nuxt-module.md
  • plans/README.md
💤 Files with no reviewable changes (1)
  • plans/003-fix-uninstall-nuxt-module.md

Comment thread packages/devtools/src/utils/nuxt-config.ts Outdated
Reassigning config.modules from filter() makes magicast regenerate the
array from scratch, discarding comments/formatting on the entries that
remain. Remove matching entries with an in-place, reverse-order splice
instead, per review feedback on nuxt#1019.
@antfu
antfu merged commit 7293498 into nuxt:main Jul 14, 2026
5 of 7 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

Development

Successfully merging this pull request may close these issues.

2 participants