Skip to content

feat(nuxi): add module remove command#1306

Open
Flo0806 wants to merge 6 commits into
nuxt:mainfrom
Flo0806:feat/module-remove
Open

feat(nuxi): add module remove command#1306
Flo0806 wants to merge 6 commits into
nuxt:mainfrom
Flo0806:feat/module-remove

Conversation

@Flo0806
Copy link
Copy Markdown
Member

@Flo0806 Flo0806 commented May 7, 2026

🔗 Linked issue

Resolves: #1184

📚 Description

Added a module remove command to remove modules from Nuxts modules array, including uninstallation from package.json (along with peer dependencies, provided they are not required by other packages).

Possible params: skipConfig, skipUninstall

Tests

Added tests , similar to add command

@Flo0806 Flo0806 requested a review from danielroe as a code owner May 7, 2026 12:42
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 7, 2026

  • nuxt-cli-playground

    npm i https://pkg.pr.new/create-nuxt@1306
    
    npm i https://pkg.pr.new/nuxi@1306
    
    npm i https://pkg.pr.new/@nuxt/cli@1306
    

commit: ede7d76

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 7, 2026

Codecov Report

❌ Patch coverage is 62.11180% with 61 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@9dc5bcc). Learn more about missing BASE report.

Files with missing lines Patch % Lines
packages/nuxi/src/commands/module/remove.ts 61.87% 49 Missing and 12 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1306   +/-   ##
=======================================
  Coverage        ?   52.87%           
=======================================
  Files           ?       50           
  Lines           ?     1375           
  Branches        ?      399           
=======================================
  Hits            ?      727           
  Misses          ?      529           
  Partials        ?      119           

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

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 7, 2026

Merging this PR will not alter performance

✅ 2 untouched benchmarks


Comparing Flo0806:feat/module-remove (ede7d76) with main (9dc5bcc)

Open in CodSpeed

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

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: 4711fd85-e15a-4f3d-9508-52c6beb0e4c0

📥 Commits

Reviewing files that changed from the base of the PR and between e1f383d and ede7d76.

📒 Files selected for processing (2)
  • packages/nuxi/src/commands/module/remove.ts
  • packages/nuxi/test/unit/commands/module/remove.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nuxi/src/commands/module/remove.ts

📝 Walkthrough

Walkthrough

This PR implements the nuxi module remove command to uninstall Nuxt modules from projects. The command is registered in the module command's subcommands map and provides a complete workflow: validating Nuxt presence, resolving module names (both aliases and npm package names) using the Nuxt Modules database, updating nuxt.config.ts to remove module entries, computing removal targets including orphaned peer dependencies, and uninstalling packages via the detected package manager. The implementation includes helper utilities for config parsing, module resolution, and peer dependency detection, with comprehensive unit test coverage verifying removal by alias, removal by npm package name, and flag behavior for skipUninstall and skipConfig options.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(nuxi): add module remove command' clearly and concisely describes the primary change—adding a new remove subcommand for modules.
Description check ✅ Passed The description is related to the changeset, explaining the module remove command, its parameters, and testing approach.
Linked Issues check ✅ Passed The PR implements the module remove command requested in #1184, updating nuxt.config and uninstalling packages with peer dependency handling.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the module remove command: adding the command entry, implementation, and tests; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/nuxi/test/unit/commands/module/remove.spec.ts (2)

27-53: 💤 Low value

runCommand and fetchModules spies are set at describe scope and never cleared

Both spies accumulate call counts across tests, and fetchModules won't re-read its mock implementation if it needs to change per-test. Since all four tests currently rely on the same stubs this doesn't cause failures, but it's fragile. Move the spy setup into beforeEach (or call .mockClear() / .mockReset() there alongside updateConfig and removeDependency).

♻️ Proposed fix
-describe('module remove', () => {
-  vi.spyOn(runCommands, 'runCommand').mockImplementation(vi.fn())
-  vi.spyOn(utils, 'fetchModules').mockResolvedValue([...])
-
-  beforeEach(() => {
-    updateConfig.mockClear()
-    removeDependency.mockClear()
-  })
+describe('module remove', () => {
+  beforeEach(() => {
+    updateConfig.mockClear()
+    removeDependency.mockClear()
+    vi.spyOn(runCommands, 'runCommand').mockImplementation(vi.fn())
+    vi.spyOn(utils, 'fetchModules').mockResolvedValue([...])
+  })
🤖 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/nuxi/test/unit/commands/module/remove.spec.ts` around lines 27 - 53,
The spies for runCommands.runCommand and utils.fetchModules are created at
describe scope and retain state between tests; move their setup into a
beforeEach (or call .mockClear()/.mockReset() there) so each test gets a fresh
spy and fetchModules can be re-mocked per-test; also ensure you clear mocks for
related functions like updateConfig and removeDependency in the same beforeEach
to avoid accumulated call counts and stale implementations when tests change
behavior.

7-7: 🏗️ Heavy lift

updateConfig mock bypasses onUpdate — the config-removal logic is entirely untested

The mock resolves immediately without ever calling the onUpdate callback. As a result, removedFromConfig stays empty in every test, readModuleName, the reverse-splice loop (lines 159-166 of remove.ts), and the multiselect picker path are never exercised. The tests only validate the uninstall-from-disk flow.

To test the config-update logic, the mock should capture and invoke the callback:

♻️ Proposed fix
-const updateConfig = vi.fn(() => Promise.resolve())
+const updateConfig = vi.fn(async (opts: { onUpdate?: (config: any) => Promise<void>, onCreate?: () => boolean | void }) => {
+  if (opts.onUpdate) {
+    await opts.onUpdate({ modules: ['@nuxt/content'] })
+  }
+})

Also applies to: 17-17

🤖 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/nuxi/test/unit/commands/module/remove.spec.ts` at line 7, The test's
updateConfig mock currently resolves without invoking the module removal
callback, so the config-removal path (onUpdate callback) is never exercised;
change the vi.fn mock for updateConfig to accept the same args as the real
function and call the provided onUpdate callback with a simulated config and
return its result so removedFromConfig gets populated; specifically, update the
mock referenced as updateConfig in the spec to call the onUpdate param
(triggering readModuleName, the reverse-splice logic in remove.ts, and the
multiselect picker path) and return the resulting Promise so the tests exercise
both config-update and disk-uninstall flows.
🤖 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/nuxi/src/commands/module/remove.ts`:
- Around line 127-166: The code assumes config.modules is always an array
causing a TypeError when it's undefined; ensure safe handling by normalizing
config.modules to an array before reading or mutating it (e.g., if
(!Array.isArray(config.modules)) config.modules = []), then build the present
list with readModuleName from config.modules, use modules.length / multiselect
logic as-is, and safely iterate backwards over config.modules to splice and push
into removedFromConfig; this keeps the rest of the flow (multiselect, isCancel,
toRemove, readModuleName, removedFromConfig) unchanged while preventing
undefined access.
- Around line 153-166: The removal logic compares resolved npm names in toRemove
to raw config entries from readModuleName, so alias entries like 'content' won't
match and remain in nuxt.config after uninstall; update removeModules to compare
resolved names by either (A) resolving each config entry with resolveModule (or
using modulesDB/installedNames) before checking (i.e., call resolveModule on the
value returned by readModuleName and compare that resolved name to toRemove) or
(B) populate toRemove with both the original and resolved package names so the
existing loop that uses readModuleName finds matches; update references to
toRemove, readModuleName, resolveModule, removeModules, and
modulesDB/installedNames accordingly.
- Around line 235-244: The removeDependency rejection is currently only logged
and the function returns true, causing the caller to still run prepare; change
the error handling in the removeDependency call so that after logging you either
re-throw the error or return false to propagate failure to the caller (so caller
honor prepare skip when ctx.args.skipUninstall is false), and update the calling
code that checks the boolean result to skip calling prepare when false; also
confirm nypm.removeDependency's signature — if it does not accept string[] then
iterate over allToRemove and call removeDependency(name, options) for each entry
(or use Promise.all on per-name calls) instead of passing the array. Ensure
references to removeDependency, prepare, ctx.args.skipUninstall and allToRemove
are updated accordingly.

---

Nitpick comments:
In `@packages/nuxi/test/unit/commands/module/remove.spec.ts`:
- Around line 27-53: The spies for runCommands.runCommand and utils.fetchModules
are created at describe scope and retain state between tests; move their setup
into a beforeEach (or call .mockClear()/.mockReset() there) so each test gets a
fresh spy and fetchModules can be re-mocked per-test; also ensure you clear
mocks for related functions like updateConfig and removeDependency in the same
beforeEach to avoid accumulated call counts and stale implementations when tests
change behavior.
- Line 7: The test's updateConfig mock currently resolves without invoking the
module removal callback, so the config-removal path (onUpdate callback) is never
exercised; change the vi.fn mock for updateConfig to accept the same args as the
real function and call the provided onUpdate callback with a simulated config
and return its result so removedFromConfig gets populated; specifically, update
the mock referenced as updateConfig in the spec to call the onUpdate param
(triggering readModuleName, the reverse-splice logic in remove.ts, and the
multiselect picker path) and return the resulting Promise so the tests exercise
both config-update and disk-uninstall flows.
🪄 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: 69293820-fddb-4912-b24a-6ce800fe37f7

📥 Commits

Reviewing files that changed from the base of the PR and between d6b1115 and e1f383d.

📒 Files selected for processing (3)
  • packages/nuxi/src/commands/module/index.ts
  • packages/nuxi/src/commands/module/remove.ts
  • packages/nuxi/test/unit/commands/module/remove.spec.ts

Comment thread packages/nuxi/src/commands/module/remove.ts Outdated
Comment thread packages/nuxi/src/commands/module/remove.ts
Comment thread packages/nuxi/src/commands/module/remove.ts
@danielroe
Copy link
Copy Markdown
Member

we've deprecated module add -> add so I'm not sure whether this would be a good idea

wdyt @atinux?

@atinux
Copy link
Copy Markdown
Member

atinux commented May 15, 2026

I guess it could be nuxt rm directly no?

@Flo0806
Copy link
Copy Markdown
Member Author

Flo0806 commented May 27, 2026

I'll change it to nuxt rm. But I think optional "remove" sounds good (more intuitive), isn't it? @danielroe @atinux

@atinux
Copy link
Copy Markdown
Member

atinux commented Jun 3, 2026

Good for me @Flo0806

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.

[Feature Request]: Support for extra nuxt module commands

4 participants