fix(server): share Windows launch handling across providers
This commit is contained in:
@@ -35,7 +35,7 @@ async function enumerateCandidates(name: string): Promise<string[]> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindowsCommandScript(executablePath: string): boolean {
|
export function isWindowsCommandScript(executablePath: string): boolean {
|
||||||
const extension = extname(executablePath).toLowerCase();
|
const extension = extname(executablePath).toLowerCase();
|
||||||
return process.platform === "win32" && (extension === ".cmd" || extension === ".bat");
|
return process.platform === "win32" && (extension === ".cmd" || extension === ".bat");
|
||||||
}
|
}
|
||||||
@@ -141,8 +141,11 @@ function escapeWindowsCmdValue(value: string): string {
|
|||||||
const unquoted = isQuoted ? value.slice(1, -1) : value;
|
const unquoted = isQuoted ? value.slice(1, -1) : value;
|
||||||
const escaped = unquoted.replace(/%/g, "%%").replace(/([&|^<>()!])/g, "^$1");
|
const escaped = unquoted.replace(/%/g, "%%").replace(/([&|^<>()!])/g, "^$1");
|
||||||
|
|
||||||
if (isQuoted || escaped.includes(" ")) {
|
if (isQuoted || /[\s"]/u.test(unquoted)) {
|
||||||
return `"${escaped}"`;
|
const quoted = escaped
|
||||||
|
.replace(/(\\*)"/g, (_match, slashes: string) => `${slashes}${slashes}\\"`)
|
||||||
|
.replace(/\\+$/u, (slashes) => `${slashes}${slashes}`);
|
||||||
|
return `"${quoted}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return escaped;
|
return escaped;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { execFile, spawn, type ChildProcess, type SpawnOptions } from "node:child_process";
|
import { execFile, spawn, type ChildProcess, type SpawnOptions } from "node:child_process";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
import { quoteWindowsArgument, quoteWindowsCommand } from "./executable.js";
|
import { isWindowsCommandScript, quoteWindowsArgument, quoteWindowsCommand } from "./executable.js";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
@@ -24,13 +24,15 @@ export function spawnProcess(
|
|||||||
options?: SpawnOptions,
|
options?: SpawnOptions,
|
||||||
): ChildProcess {
|
): ChildProcess {
|
||||||
const isWindows = process.platform === "win32";
|
const isWindows = process.platform === "win32";
|
||||||
|
const shell = isWindowsCommandScript(command) ? true : (options?.shell ?? isWindows);
|
||||||
|
|
||||||
const resolvedCommand = isWindows ? quoteWindowsCommand(command) : command;
|
const shouldQuoteForShell = isWindows && shell !== false;
|
||||||
const resolvedArgs = isWindows ? args.map(quoteWindowsArgument) : args;
|
const resolvedCommand = shouldQuoteForShell ? quoteWindowsCommand(command) : command;
|
||||||
|
const resolvedArgs = shouldQuoteForShell ? args.map(quoteWindowsArgument) : args;
|
||||||
|
|
||||||
return spawn(resolvedCommand, resolvedArgs, {
|
return spawn(resolvedCommand, resolvedArgs, {
|
||||||
...options,
|
...options,
|
||||||
shell: options?.shell ?? isWindows,
|
shell,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -41,8 +43,10 @@ export async function execCommand(
|
|||||||
options?: ExecCommandOptions,
|
options?: ExecCommandOptions,
|
||||||
): Promise<ExecCommandResult> {
|
): Promise<ExecCommandResult> {
|
||||||
const isWindows = process.platform === "win32";
|
const isWindows = process.platform === "win32";
|
||||||
const resolvedCommand = isWindows ? quoteWindowsCommand(command) : command;
|
const shell = isWindowsCommandScript(command) ? true : isWindows;
|
||||||
const resolvedArgs = isWindows ? args.map(quoteWindowsArgument) : args;
|
const shouldQuoteForShell = isWindows && shell !== false;
|
||||||
|
const resolvedCommand = shouldQuoteForShell ? quoteWindowsCommand(command) : command;
|
||||||
|
const resolvedArgs = shouldQuoteForShell ? args.map(quoteWindowsArgument) : args;
|
||||||
|
|
||||||
return execFileAsync(resolvedCommand, resolvedArgs, {
|
return execFileAsync(resolvedCommand, resolvedArgs, {
|
||||||
cwd: options?.cwd,
|
cwd: options?.cwd,
|
||||||
@@ -50,7 +54,7 @@ export async function execCommand(
|
|||||||
encoding: options?.encoding ?? "utf8",
|
encoding: options?.encoding ?? "utf8",
|
||||||
timeout: options?.timeout,
|
timeout: options?.timeout,
|
||||||
maxBuffer: options?.maxBuffer,
|
maxBuffer: options?.maxBuffer,
|
||||||
shell: isWindows,
|
shell,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
}) as Promise<ExecCommandResult>;
|
}) as Promise<ExecCommandResult>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user