Skip to content

Commit 13522d4

Browse files
committed
feat(dlx): add cross-platform binary resolution
Implement Windows binary wrapper resolution to match npm/npx behavior. Critical for production-ready Windows support. Changes: - Added resolveBinaryPath() helper function - On Windows: checks for .cmd, .bat, .ps1, .exe wrappers in priority order - On Unix: uses path directly - Order matches npm bin-links creation strategy - Updated findBinaryPath() to resolve platform-specific wrappers - Calls resolveBinaryPath() after determining raw binary path - Ensures correct executable is found on Windows - Enhanced executePackage() with Windows shell handling - Detects .bat/.cmd/.ps1 files and sets shell: true - Matches npm/npx execution behavior - .exe files don't need shell mode Aligns with npm/npx binary resolution: 1. npm creates platform-specific wrappers in node_modules/.bin/ 2. On Windows: .cmd (CMD), .ps1 (PowerShell), bare (Git Bash) 3. On Unix: shell script wrapper 4. Socket now resolves these correctly Fixes Windows compatibility gap identified in dlx code review.
1 parent 62b1e47 commit 13522d4

1 file changed

Lines changed: 52 additions & 3 deletions

File tree

src/dlx-package.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,40 @@ async function ensurePackageInstalled(
287287
)
288288
}
289289

290+
/**
291+
* Resolve binary path with cross-platform wrapper support.
292+
* On Windows, checks for .cmd, .bat, .ps1, .exe wrappers in order.
293+
* On Unix, uses path directly.
294+
*
295+
* Aligns with npm/npx binary resolution strategy.
296+
*/
297+
function resolveBinaryPath(basePath: string): string {
298+
const fs = getFs()
299+
300+
if (!WIN32) {
301+
// Unix: use path directly
302+
return basePath
303+
}
304+
305+
// Windows: check for wrappers in priority order
306+
// Order matches npm bin-links creation: .cmd, .ps1, .exe, then bare
307+
const extensions = ['.cmd', '.bat', '.ps1', '.exe', '']
308+
309+
for (const ext of extensions) {
310+
const testPath = basePath + ext
311+
if (fs.existsSync(testPath)) {
312+
return testPath
313+
}
314+
}
315+
316+
// Fallback to original path if no wrapper found
317+
return basePath
318+
}
319+
290320
/**
291321
* Find the binary path for an installed package.
292322
* Intelligently handles packages with single or multiple binaries.
323+
* Resolves platform-specific wrappers (.cmd, .ps1, etc.) on Windows.
293324
*/
294325
function findBinaryPath(
295326
packageDir: string,
@@ -348,7 +379,10 @@ function findBinaryPath(
348379
throw new Error(`No binary found for package "${packageName}"`)
349380
}
350381

351-
return normalizePath(path.join(installedDir, binPath))
382+
const rawPath = normalizePath(path.join(installedDir, binPath))
383+
384+
// Resolve platform-specific wrapper (Windows .cmd/.ps1/etc.)
385+
return resolveBinaryPath(rawPath)
352386
}
353387

354388
/**
@@ -462,9 +496,12 @@ export async function downloadPackage(
462496
}
463497

464498
/**
465-
* Execute a package's binary.
499+
* Execute a package's binary with cross-platform shell handling.
466500
* The package must already be installed (use downloadPackage first).
467501
*
502+
* On Windows, script files (.bat, .cmd, .ps1) require shell: true.
503+
* Matches npm/npx execution behavior.
504+
*
468505
* @example
469506
* ```typescript
470507
* // Execute an already-installed package
@@ -482,5 +519,17 @@ export function executePackage(
482519
spawnOptions?: SpawnOptions | undefined,
483520
spawnExtra?: SpawnExtra | undefined,
484521
): ReturnType<typeof spawn> {
485-
return spawn(binaryPath, args, spawnOptions, spawnExtra)
522+
// On Windows, script files (.bat, .cmd, .ps1) require shell: true
523+
// because they are not executable on their own and must be run through cmd.exe.
524+
// .exe files are actual binaries and don't need shell mode.
525+
const needsShell = WIN32 && /\.(?:bat|cmd|ps1)$/i.test(binaryPath)
526+
527+
const finalOptions = needsShell
528+
? {
529+
...spawnOptions,
530+
shell: true,
531+
}
532+
: spawnOptions
533+
534+
return spawn(binaryPath, args, finalOptions, spawnExtra)
486535
}

0 commit comments

Comments
 (0)