Skip to content

Commit 12a3695

Browse files
committed
Refactor WSL implementation
1 parent c3aa28f commit 12a3695

File tree

3 files changed

+27
-107
lines changed

3 files changed

+27
-107
lines changed

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async function platformSpecificImplementation() {
7777
}
7878

7979
case 'linux': {
80-
const {default: isWsl} = await import('is-wsl');
80+
const {isWsl} = await import('wsl-utils');
8181
if (isWsl) {
8282
return import('./lib/wsl.js');
8383
}

lib/wsl.js

Lines changed: 24 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,51 @@
11
import {Buffer} from 'node:buffer';
2-
import {execFile} from 'node:child_process';
3-
import {promisify} from 'node:util';
42
import chunkify from '@sindresorhus/chunkify';
5-
6-
const execFilePromise = promisify(execFile);
7-
8-
// TODO: I plan to move these to wsl-extras at some point.
9-
10-
/**
11-
Convert WSL paths to Windows paths using `wslpath`.
12-
13-
@param {string[]} paths - Array of WSL/Linux paths..
14-
@returns {Promise<string[]>} Array of Windows paths.
15-
*/
16-
async function wslPathToWindows(paths) {
17-
try {
18-
const {stdout} = await execFilePromise('wslpath', ['-w', '-a', ...paths]);
19-
return stdout.split(/\r?\n/).filter(Boolean);
20-
} catch (error) {
21-
error.message = `Failed to convert paths with "wslpath": ${error.message}`;
22-
throw error;
23-
}
24-
}
25-
26-
/**
27-
Check if a Windows path is a UNC path (\\wsl$\...).
28-
29-
@param {string} path - Windows path to check.
30-
@returns {boolean} True if path is UNC.
31-
*/
32-
function isUncPath(path) {
33-
return /^\\\\/u.test(path);
34-
}
35-
36-
/**
37-
Partition paths into local Windows paths and UNC WSL paths.
38-
39-
@param {string[]} windowsPaths - Array of Windows paths.
40-
@param {string[]} originalPaths - Corresponding original Linux paths.g
41-
@returns {{localPaths: string[], uncPaths: string[]}} Partitioned paths.
42-
*/
43-
function partitionWindowsPaths(windowsPaths, originalPaths) {
44-
const localPaths = [];
45-
const uncPaths = [];
46-
47-
for (const [index, windowsPath] of windowsPaths.entries()) {
48-
if (isUncPath(windowsPath)) {
49-
uncPaths.push(originalPaths[index]);
50-
} else {
51-
localPaths.push(windowsPath);
52-
}
53-
}
54-
55-
return {localPaths, uncPaths};
56-
}
57-
58-
/**
59-
Execute a PowerShell script using -EncodedCommand for safety.
60-
61-
@param {string} script - PowerShell script to execute.
62-
@returns {Promise<{stdout: string, stderr: string}>} Execution result.
63-
*/
64-
async function executePowerShellScript(script) {
65-
const encodedCommand = Buffer.from(script, 'utf16le').toString('base64');
66-
return execFilePromise('powershell.exe', [
67-
'-NoProfile',
68-
'-NonInteractive',
69-
'-ExecutionPolicy',
70-
'Bypass',
71-
'-EncodedCommand',
72-
encodedCommand,
73-
]);
74-
}
75-
76-
/**
77-
Check if WSL interop is enabled by testing PowerShell availability.
78-
79-
@returns {Promise<boolean>} True if interop is enabled
80-
*/
81-
async function isWslInteropEnabled() {
82-
try {
83-
await execFilePromise('powershell.exe', [
84-
'-NoProfile',
85-
'-NonInteractive',
86-
'-Command',
87-
'$PSVersionTable.PSVersion',
88-
]);
89-
return true;
90-
} catch (error) {
91-
if (error.code === 'ENOENT') {
92-
return false;
93-
}
94-
95-
throw error;
96-
}
97-
}
3+
import {canAccessPowerShell, convertWslPathToWindows, isUncPath} from 'wsl-utils';
4+
import {executePowerShell} from 'powershell-utils';
985

996
/**
1007
WSL implementation:
101-
- Converts WSL paths to Windows paths with `wslpath -w -a`
8+
- Converts WSL paths to Windows paths
1029
- For Windows-local paths (e.g., `C:\…`), uses PowerShell to send to Recycle Bin
10310
- For UNC `\\wsl$\…` paths (Linux filesystem), falls back to the Linux trash implementation
10411
- Processes inputs in chunks to avoid command-line length limits
10512
- Uses `-LiteralPath` to avoid wildcard expansion
10613
- Uses `-EncodedCommand` with UTF-16LE to avoid quoting/length issues
10714
*/
10815
export default async function wsl(paths) {
109-
// Check interop availability once
110-
const interopEnabled = await isWslInteropEnabled();
16+
const interopEnabled = await canAccessPowerShell();
11117
if (!interopEnabled) {
11218
const error = new Error('WSL interop is disabled. Enable it or use Linux trash implementation.');
11319
error.code = 'WSL_INTEROP_DISABLED';
11420
throw error;
11521
}
11622

23+
let linuxTrash;
24+
11725
for (const chunk of chunkify(paths, 400)) {
118-
// Resolve to Windows paths
11926
// eslint-disable-next-line no-await-in-loop
120-
const windowsPathsRaw = await wslPathToWindows(chunk);
27+
const windowsPaths = await convertWslPathToWindows(chunk);
12128

12229
// Partition into local drive paths and UNC \\wsl$ paths
123-
const {localPaths: localWindowsPaths, uncPaths: uncLinuxPaths} = partitionWindowsPaths(windowsPathsRaw, chunk);
30+
const localWindowsPaths = [];
31+
const uncLinuxPaths = [];
32+
33+
for (const [index, windowsPath] of windowsPaths.entries()) {
34+
if (isUncPath(windowsPath)) {
35+
uncLinuxPaths.push(chunk[index]);
36+
} else {
37+
localWindowsPaths.push(windowsPath);
38+
}
39+
}
12440

12541
// Fallback to Linux trash for files that live on the Linux filesystem (UNC \\wsl$)
12642
if (uncLinuxPaths.length > 0) {
127-
// eslint-disable-next-line no-await-in-loop
128-
const {default: linuxTrash} = await import('./linux.js');
43+
if (!linuxTrash) {
44+
// eslint-disable-next-line no-await-in-loop
45+
const {default: linuxTrashImport} = await import('./linux.js');
46+
linuxTrash = linuxTrashImport;
47+
}
48+
12949
// eslint-disable-next-line no-await-in-loop
13050
await linuxTrash(uncLinuxPaths);
13151
}
@@ -157,8 +77,7 @@ foreach ($p in $paths) {
15777
}
15878
`.trim();
15979

160-
// Execute PowerShell
16180
// eslint-disable-next-line no-await-in-loop
162-
await executePowerShellScript(psScript);
81+
await executePowerShell(psScript);
16382
}
16483
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@
5050
"chunkify": "^5.0.0",
5151
"globby": "^14.1.0",
5252
"is-path-inside": "^4.0.0",
53-
"is-wsl": "^3.1.0",
5453
"move-file": "^3.1.0",
5554
"p-map": "^7.0.3",
55+
"powershell-utils": "^0.2.0",
56+
"wsl-utils": "^0.4.0",
5657
"xdg-trashdir": "^3.1.0"
5758
},
5859
"devDependencies": {

0 commit comments

Comments
 (0)