fix: normalize windows runtime shim executables

This commit is contained in:
Peter Steinberger
2026-03-09 07:01:42 +00:00
parent 912aa8744a
commit 66c581c64c
2 changed files with 27 additions and 6 deletions

View File

@@ -0,0 +1,16 @@
import { describe, expect, test } from "vitest";
import { normalizeExecutableToken } from "./exec-wrapper-resolution.js";
describe("normalizeExecutableToken", () => {
test("strips common windows executable suffixes", () => {
expect(normalizeExecutableToken("bun.cmd")).toBe("bun");
expect(normalizeExecutableToken("deno.bat")).toBe("deno");
expect(normalizeExecutableToken("pwsh.com")).toBe("pwsh");
expect(normalizeExecutableToken("cmd.exe")).toBe("cmd");
});
test("normalizes path-qualified windows shims", () => {
expect(normalizeExecutableToken("C:\\tools\\bun.cmd")).toBe("bun");
expect(normalizeExecutableToken("/tmp/deno.exe")).toBe("deno");
});
});

View File

@@ -7,7 +7,7 @@ import {
export const MAX_DISPATCH_WRAPPER_DEPTH = 4;
const WINDOWS_EXE_SUFFIX = ".exe";
const WINDOWS_EXECUTABLE_SUFFIXES = [".exe", ".cmd", ".bat", ".com"] as const;
const POSIX_SHELL_WRAPPER_NAMES = ["ash", "bash", "dash", "fish", "ksh", "sh", "zsh"] as const;
const WINDOWS_CMD_WRAPPER_NAMES = ["cmd"] as const;
@@ -31,13 +31,18 @@ function withWindowsExeAliases(names: readonly string[]): string[] {
const expanded = new Set<string>();
for (const name of names) {
expanded.add(name);
expanded.add(`${name}${WINDOWS_EXE_SUFFIX}`);
expanded.add(`${name}.exe`);
}
return Array.from(expanded);
}
function stripWindowsExeSuffix(value: string): string {
return value.endsWith(WINDOWS_EXE_SUFFIX) ? value.slice(0, -WINDOWS_EXE_SUFFIX.length) : value;
function stripWindowsExecutableSuffix(value: string): string {
for (const suffix of WINDOWS_EXECUTABLE_SUFFIXES) {
if (value.endsWith(suffix)) {
return value.slice(0, -suffix.length);
}
}
return value;
}
export const POSIX_SHELL_WRAPPERS = new Set(POSIX_SHELL_WRAPPER_NAMES);
@@ -115,7 +120,7 @@ export function basenameLower(token: string): string {
}
export function normalizeExecutableToken(token: string): string {
return stripWindowsExeSuffix(basenameLower(token));
return stripWindowsExecutableSuffix(basenameLower(token));
}
export function isDispatchWrapperExecutable(token: string): boolean {
@@ -132,7 +137,7 @@ function normalizeRawCommand(rawCommand?: string | null): string | null {
}
function findShellWrapperSpec(baseExecutable: string): ShellWrapperSpec | null {
const canonicalBase = stripWindowsExeSuffix(baseExecutable);
const canonicalBase = stripWindowsExecutableSuffix(baseExecutable);
for (const spec of SHELL_WRAPPER_SPECS) {
if (spec.names.has(canonicalBase)) {
return spec;