Skip to content

feat: add pnpm clean command for safe node_modules/lockfile removal#10708

Merged
zkochan merged 4 commits intomainfrom
clean
Feb 28, 2026
Merged

feat: add pnpm clean command for safe node_modules/lockfile removal#10708
zkochan merged 4 commits intomainfrom
clean

Conversation

@zkochan
Copy link
Copy Markdown
Member

@zkochan zkochan commented Feb 27, 2026

Adds a new pnpm clean command that safely removes node_modules contents from all workspace projects. Uses Node.js fs.rm() which correctly handles NTFS junctions on Windows without following them into their targets, preventing catastrophic data loss. Preserves non-pnpm hidden files (e.g. .cache) and lockfiles by default; use --lockfile/-l to also remove pnpm-lock.yaml files. Also cleans custom virtual-store-dir when configured inside the project root.

close #10707
close #6816

zkochan and others added 2 commits February 27, 2026 11:25
Adds a new `pnpm clean` command that safely removes node_modules contents
from all workspace projects. Uses Node.js fs.rm() which correctly handles
NTFS junctions on Windows without following them into their targets,
preventing catastrophic data loss. Preserves non-pnpm hidden files
(e.g. .cache) and lockfiles by default; use --lockfile/-l to also
remove pnpm-lock.yaml files. Also cleans custom virtual-store-dir
when configured inside the project root.

Closes #10707
…istence before printing

- Replace custom isSubdir function with the existing is-subdir package
- Only print "Removing" and remove lockfile/virtualStoreDir when they exist
- Rethrow non-ENOENT errors instead of swallowing them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zkochan zkochan marked this pull request as ready for review February 27, 2026 12:08
Copilot AI review requested due to automatic review settings February 27, 2026 12:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new pnpm clean command that safely removes node_modules contents from all workspace projects to address catastrophic data loss issues on Windows caused by NTFS junctions when using standard deletion commands like rm -rf or Remove-Item -Recurse. The command uses @zkochan/rimraf which leverages Node.js's fs.rm() to correctly handle junctions without following them into their targets.

Changes:

  • Adds pnpm clean command with --lockfile option to optionally remove pnpm-lock.yaml files
  • Implements safe removal that preserves non-pnpm hidden files (e.g., .cache) while removing pnpm-specific entries (.pnpm, .bin, .modules.yaml) and regular packages
  • Handles custom virtual-store-dir configurations when located inside the project root

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm/src/cmd/clean.ts New command implementation with handler, help text, and helper functions for safe directory removal
pnpm/src/cmd/index.ts Registers the new clean command in the command list
pnpm/src/cmd/help.ts Adds clean command to the advanced commands help section
pnpm/test/clean.ts Comprehensive test suite covering single project, workspace, lockfile handling, and virtual store scenarios
pnpm/package.json Adds is-subdir dependency for path validation
pnpm-lock.yaml Updates lockfile with is-subdir dependency
cspell.json Adds "ntfs" to dictionary for spell checking
.changeset/safe-clean-command.md Documents the new feature for release notes
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

zkochan and others added 2 commits February 27, 2026 13:25
…sDirContents

- Replace manual fs.access + try/catch with pathExists for cleaner existence checks
- Add ENOENT handling to removeModulesDirContents readdir to handle the race
  where the directory is removed between hasContentsToRemove and readdir

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…emoveLockfile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zkochan zkochan requested a review from a team February 27, 2026 12:52
@zkochan zkochan changed the title feat: add pnpm clean command for safe node_modules removal feat: add pnpm clean command for safe node_modules/lockfile removal Feb 27, 2026
@@ -0,0 +1,141 @@
import { promises as fs } from 'fs'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why not import the Promise version of fs directly?

Suggested change
import { promises as fs } from 'fs'
import fs from 'node:fs/promises'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It doesn't make a difference. Currently the way I added it is the way it is used in other places in the repo.

@@ -0,0 +1,141 @@
import { promises as fs } from 'fs'
import path from 'path'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Would it generally not be preferred to use the node: prefix for internal modules?

Suggested change
import path from 'path'
import path from 'node:path'

Copy link
Copy Markdown
Member Author

@zkochan zkochan Feb 27, 2026

Choose a reason for hiding this comment

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

That is not the convention currently used in the repo.

@Mister-Hope
Copy link
Copy Markdown

See #6816 (comment)

I found this new command while maintaining docs translations.

I think at least we may need to evaluate the impact by counting the percentage of repos that is using clean script as repo state cleanup.

@Mister-Hope
Copy link
Copy Markdown

Mister-Hope commented Mar 26, 2026

E.g.: As a Vue Ecoystem maintainer, our main repo vue3 has a clean script: https://github.com/vuejs/core/blob/main/package.json

Since pnpm clean destorys the whole node_modules and even removes lock-file, it would take time to recover if someone wants a clean script run, but types pnpm clean by mistake (previous behavior).

Under this point, I would suggest at least change it to pnpm cleanup. It's not late to adjust such things in beta stage.

@pengzhanbo
Copy link
Copy Markdown

I am not optimistic about the clean naming. In most of my projects, there is usually a clean script command to clean up the project's build output. clean has almost become a habit for me. I think you should at least investigate the proportion of clean usage in projects using pnpm, whether users can accept it, whether it will affect existing projects, the scope of impact, and other factors. Directly adding this command, without users fully understanding it, could potentially lead to disastrous consequences.

@btea
Copy link
Copy Markdown
Member

btea commented Mar 27, 2026

I think they make a good point; the clean command is indeed very common in modern front-end tools and related projects.

Perhaps we should consider a more fitting name to avoid any unintended negative effects on some projects. @zkochan

@jonkoops
Copy link
Copy Markdown

I would suggest keeping all the discussion centralized, there is also a thread on #6816

@btea
Copy link
Copy Markdown
Member

btea commented Mar 27, 2026

@jonkoops Thanks for reminding me, I think I missed it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

6 participants