<# .SYNOPSIS Thorough removal of Node.js, npm, global modules, shims, and n8n. .DESCRIPTION Removes all Node.js and npm-related components from Windows. Backs up ~/.n8n before removing it. Safe to run repeatedly. Fully idempotent. #> $ErrorActionPreference = "Stop" Write-Host "=== Full Uninstall: Node.js, npm, n8n ===" # --------------------------------------------------------------- # PRE-CLEANUP STATE REPORT # --------------------------------------------------------------- function Get-SystemState { Write-Host "" Write-Host "=== System State Before Cleanup ===" # 1. Node.js MSI entries $unKeys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" ) $msiEntries = Get-ItemProperty -Path $unKeys -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Node.js*" } Write-Host "Node MSI entries: $($msiEntries.Count)" # 2. Node folders $nodeFolders = @( "C:\Program Files\nodejs", "C:\Program Files (x86)\nodejs", "$env:LOCALAPPDATA\Programs\nodejs" ) | Where-Object { Test-Path $_ } Write-Host "Node folders: $($nodeFolders.Count)" # 3. npm folders $npmFolders = @( "$env:APPDATA\npm", "$env:APPDATA\npm-cache", "$env:USERPROFILE\.npm" ) | Where-Object { Test-Path $_ } Write-Host "npm folders: $($npmFolders.Count)" # 4. Shims in PATH $shimNames = @("npm.cmd","npm.ps1","npx.cmd","npx.ps1","corepack.cmd","corepack.ps1","n8n.cmd","n8n.ps1") $shimHits = @() foreach ($dir in ($env:PATH -split ";")) { foreach ($name in $shimNames) { $cand = Join-Path $dir $name if (Test-Path $cand) { $shimHits += $cand } } } Write-Host "Shims in PATH: $($shimHits.Count)" # 5. Stray executables $exeNames = @("node","npm","npx","corepack","n8n") $exeHits = @() foreach ($name in $exeNames) { $cmd = Get-Command $name -ErrorAction SilentlyContinue if ($cmd) { $exeHits += $cmd.Source } } Write-Host "Stray executables: $($exeHits.Count)" # 6. .n8n folder $n8nDir = Join-Path $env:USERPROFILE ".n8n" $exists = Test-Path $n8nDir Write-Host ".n8n directory: $([bool]$exists)" Write-Host "===================================" # If nothing requires removal, exit immediately. if ( $msiEntries.Count -eq 0 -and $nodeFolders.Count -eq 0 -and $npmFolders.Count -eq 0 -and $shimHits.Count -eq 0 -and $exeHits.Count -eq 0 -and -not (Test-Path $n8nDir) ) { Write-Host "Nothing to remove. System is already clean." exit } # Otherwise ask user before performing destructive cleanup. $choice = Read-Host "Proceed with aggressive cleanup? (Y/N)" if ($choice -notmatch "^[Yy]$") { Write-Host "Aborted by user." exit } } # Run diagnostic before uninstall begins Get-SystemState # --------------------------------------------------------------- # BACKUP .n8n BEFORE REMOVAL # --------------------------------------------------------------- function Backup-N8N { $n8nDir = Join-Path $env:USERPROFILE ".n8n" if (-not (Test-Path $n8nDir)) { Write-Host "No .n8n directory found. Skipping backup." return } Write-Host "Backing up .n8n before removal..." $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" $backupRoot = Join-Path $env:USERPROFILE "Desktop\n8n-backups" $zipPath = Join-Path $backupRoot "n8n-backup-$timestamp.zip" $tempCopy = Join-Path $env:TEMP "n8n-backup-$timestamp" if (-not (Test-Path $backupRoot)) { New-Item -ItemType Directory -Path $backupRoot | Out-Null } if (Test-Path $tempCopy) { Remove-Item $tempCopy -Recurse -Force } New-Item -ItemType Directory -Path $tempCopy | Out-Null try { Copy-Item "$n8nDir\*" $tempCopy -Recurse -Force Compress-Archive -Path $tempCopy -DestinationPath $zipPath -Force } finally { if (Test-Path $tempCopy) { Remove-Item $tempCopy -Recurse -Force } } $sizeMB = [math]::Round((Get-Item $zipPath).Length / 1MB, 1) Write-Host "Backup complete ($sizeMB MB) --> $zipPath" Write-Host "Removing .n8n directory..." Remove-Item $n8nDir -Recurse -Force -ErrorAction SilentlyContinue } # --------------------------------------------------------------- # REMOVE n8n (GLOBAL MODULE + SHIMS) # --------------------------------------------------------------- function Remove-N8N { Write-Host "" Write-Host "Removing n8n..." $npm = Get-Command npm -ErrorAction SilentlyContinue if ($npm) { try { npm uninstall -g n8n 2>$null } catch {} } $paths = @( (Join-Path $env:APPDATA "npm\node_modules\n8n") (Join-Path $env:APPDATA "npm\n8n.cmd") (Join-Path $env:APPDATA "npm\n8n.ps1") ) foreach ($p in $paths) { if (Test-Path $p) { Write-Host "Deleting: $p" Remove-Item $p -Recurse -Force -ErrorAction SilentlyContinue } } Write-Host "n8n removal complete." } # --------------------------------------------------------------- # REMOVE NODE.JS MSI INSTALLATIONS # --------------------------------------------------------------- function Remove-NodeMSI { Write-Host "" Write-Host "Removing Node.js MSI installations..." $unKeys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" ) $products = Get-ItemProperty -Path $unKeys -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Node.js*" } foreach ($p in $products) { $u = $p.UninstallString if ($u -match "{[0-9A-Fa-f\-]+}") { $guid = [regex]::Match($u, "{[0-9A-Fa-f\-]+}").Value Write-Host "Uninstalling Node.js MSI: $guid" Start-Process msiexec.exe -Wait -ArgumentList "/x $guid /quiet /norestart" } elseif ($u) { Write-Host "Running uninstall command: $u" Start-Process cmd.exe -Wait -ArgumentList "/c `"$u`"" } } Write-Host "Node MSI removal complete." } # --------------------------------------------------------------- # REMOVE ALL NODE AND NPM FOLDERS # --------------------------------------------------------------- function Remove-NodeFolders { Write-Host "" Write-Host "Removing Node.js and npm folders..." $folders = @( "C:\Program Files\nodejs", "C:\Program Files (x86)\nodejs", "$env:LOCALAPPDATA\Programs\nodejs", "$env:APPDATA\npm", "$env:APPDATA\npm-cache", "$env:USERPROFILE\.npm", "$env:USERPROFILE\.node-gyp", "$env:USERPROFILE\.node_repl_history" ) foreach ($f in $folders) { if (Test-Path $f) { Write-Host "Deleting folder: $f" Remove-Item $f -Recurse -Force -ErrorAction SilentlyContinue } } Write-Host "Folder cleanup complete." } # --------------------------------------------------------------- # REMOVE SHIMS (npm, npx, corepack, n8n) # --------------------------------------------------------------- function Remove-Shims { Write-Host "" Write-Host "Removing npm, npx, corepack, and n8n shims..." $names = @( "npm.cmd","npm.ps1", "npx.cmd","npx.ps1", "corepack.cmd","corepack.ps1", "n8n.cmd","n8n.ps1" ) $searchDirs = ($env:PATH -split ";") foreach ($dir in $searchDirs) { foreach ($name in $names) { $cand = Join-Path $dir $name if (Test-Path $cand) { Write-Host "Removing shim: $cand" Remove-Item $cand -Force -ErrorAction SilentlyContinue } } } } # --------------------------------------------------------------- # CLEAN PATH ENTRIES # --------------------------------------------------------------- function Remove-PathEntries { Write-Host "" Write-Host "Cleaning PATH entries..." $patterns = @("nodejs","npm","node","corepack","n8n") $clean = { param($scope) $path = [Environment]::GetEnvironmentVariable("Path",$scope) if (-not $path) { return } $parts = $path -split ";" $filtered = $parts | Where-Object { $remove = $false foreach ($p in $patterns) { if ($_ -match $p) { $remove = $true } } -not $remove } $new = ($filtered -join ";") [Environment]::SetEnvironmentVariable("Path",$new,$scope) Write-Host "Cleaned $scope PATH." } $clean.Invoke("Machine") $clean.Invoke("User") $env:PATH = ( [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Environment]::GetEnvironmentVariable("Path","User") ) } # --------------------------------------------------------------- # REMOVE STRAY EXECUTABLES ANYWHERE IN PATH # --------------------------------------------------------------- function Remove-StrayExecutables { Write-Host "" Write-Host "Checking for leftover executables..." $names = @("node","npm","npx","corepack","n8n") foreach ($exe in $names) { $cmd = Get-Command $exe -ErrorAction SilentlyContinue if ($cmd) { Write-Host "Deleting stray executable: $($cmd.Source)" try { Remove-Item $cmd.Source -Force -ErrorAction SilentlyContinue } catch {} } } } # --------------------------------------------------------------- # EXECUTE ALL STEPS # --------------------------------------------------------------- Backup-N8N Remove-N8N Remove-NodeMSI Remove-NodeFolders Remove-Shims Remove-PathEntries Remove-StrayExecutables Write-Host "" Write-Host "All components removed." Write-Host "System is clean and ready for fresh installation tests."