Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ async function platformSpecificImplementation() {
return import('./lib/windows.js');
}

case 'linux': {
const {isWsl} = await import('wsl-utils');
if (isWsl) {
return import('./lib/wsl.js');
}

return import('./lib/linux.js');
}

default: {
return import('./lib/linux.js');
}
Expand Down
83 changes: 83 additions & 0 deletions lib/wsl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {Buffer} from 'node:buffer';
import chunkify from '@sindresorhus/chunkify';
import {canAccessPowerShell, convertWslPathToWindows, isUncPath} from 'wsl-utils';
import {executePowerShell} from 'powershell-utils';

/**
WSL implementation:
- Converts WSL paths to Windows paths
- For Windows-local paths (e.g., `C:\…`), uses PowerShell to send to Recycle Bin
- For UNC `\\wsl$\…` paths (Linux filesystem), falls back to the Linux trash implementation
- Processes inputs in chunks to avoid command-line length limits
- Uses `-LiteralPath` to avoid wildcard expansion
- Uses `-EncodedCommand` with UTF-16LE to avoid quoting/length issues
*/
export default async function wsl(paths) {
const interopEnabled = await canAccessPowerShell();
if (!interopEnabled) {
const error = new Error('WSL interop is disabled. Enable it or use Linux trash implementation.');
error.code = 'WSL_INTEROP_DISABLED';
throw error;
}

let linuxTrash;

for (const chunk of chunkify(paths, 400)) {
// eslint-disable-next-line no-await-in-loop
const windowsPaths = await convertWslPathToWindows(chunk);

// Partition into local drive paths and UNC \\wsl$ paths
const localWindowsPaths = [];
const uncLinuxPaths = [];

for (const [index, windowsPath] of windowsPaths.entries()) {
if (isUncPath(windowsPath)) {
uncLinuxPaths.push(chunk[index]);
} else {
localWindowsPaths.push(windowsPath);
}
}

// Fallback to Linux trash for files that live on the Linux filesystem (UNC \\wsl$)
if (uncLinuxPaths.length > 0) {
if (!linuxTrash) {
// eslint-disable-next-line no-await-in-loop
const {default: linuxTrashImport} = await import('./linux.js');
linuxTrash = linuxTrashImport;
}

// eslint-disable-next-line no-await-in-loop
await linuxTrash(uncLinuxPaths);
}

// Nothing to recycle on Windows side for this chunk
if (localWindowsPaths.length === 0) {
continue;
}

// Build a PowerShell script that:
// - Decodes a Base64 JSON array of paths
// - Uses LiteralPath to avoid wildcard expansion
// - Sends files/dirs to Recycle Bin
const json = JSON.stringify(localWindowsPaths);
const base64Json = Buffer.from(json, 'utf8').toString('base64');

const psScript = `
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName Microsoft.VisualBasic
$paths = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64Json}')) | ConvertFrom-Json
foreach ($p in $paths) {
if (Test-Path -LiteralPath $p) {
if (Test-Path -LiteralPath $p -PathType Container) {
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory($p, 'OnlyErrorDialogs', 'SendToRecycleBin')
} else {
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($p, 'OnlyErrorDialogs', 'SendToRecycleBin')
}
}
}
`.trim();

// eslint-disable-next-line no-await-in-loop
await executePowerShell(psScript);
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
"is-path-inside": "^4.0.0",
"move-file": "^3.1.0",
"p-map": "^7.0.3",
"powershell-utils": "^0.2.0",
"wsl-utils": "^0.4.0",
"xdg-trashdir": "^3.1.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ npm install --global trash-cli
On macOS, [`macos-trash`](https://github.com/sindresorhus/macos-trash) is used.\
On Linux, the [XDG spec](https://specifications.freedesktop.org/trash/1.0/) is followed.\
On Windows, [`recycle-bin`](https://github.com/sindresorhus/recycle-bin) is used.
On WSL (Windows Subsystem for Linux), files are moved to the Windows Recycle Bin.

## FAQ

Expand Down