Skip to content

Commit ee9f912

Browse files
committed
refactor: simplify shimExecFile with generic type and Reflect.apply
Replace `as unknown as` cast chains with a generic type parameter and Reflect.apply for type-safe argument forwarding. Preserve the custom promisify symbol using Reflect.get/set instead of manual symbol casts.
1 parent 12b454f commit ee9f912

2 files changed

Lines changed: 27 additions & 20 deletions

File tree

test/utils/platform.test.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ describe("platform utils", () => {
115115
describe("shimExecFile", () => {
116116
const tmp = path.join(os.tmpdir(), "vscode-coder-tests-shim");
117117
const mod = shimExecFile(cp);
118-
const shimmedExecFile = promisify(mod.execFile);
118+
const execFileAsync = promisify(mod.execFile);
119119

120120
beforeAll(async () => {
121121
await fs.rm(tmp, { recursive: true, force: true });
@@ -128,7 +128,7 @@ describe("platform utils", () => {
128128
"echo",
129129
'process.stdout.write("ok");',
130130
);
131-
const { stdout } = await shimmedExecFile(script);
131+
const { stdout } = await execFileAsync(script);
132132
expect(stdout).toBe("ok");
133133
});
134134

@@ -138,24 +138,28 @@ describe("platform utils", () => {
138138
"echo-args",
139139
"process.stdout.write(process.argv.slice(2).join(','));",
140140
);
141-
const { stdout } = await shimmedExecFile(script, ["a", "b", "c"]);
141+
const { stdout } = await execFileAsync(script, ["a", "b", "c"]);
142142
expect(stdout).toBe("a,b,c");
143143
});
144144

145145
it("does not rewrite non-.js files", async () => {
146-
await expect(shimmedExecFile("/nonexistent/binary")).rejects.toThrow(
146+
await expect(execFileAsync("/nonexistent/binary")).rejects.toThrow(
147147
"ENOENT",
148148
);
149149
});
150150

151151
it("preserves the callback form", async () => {
152-
const script = path.join(tmp, "echo.js");
152+
const script = await writeExecutable(
153+
tmp,
154+
"cb-echo",
155+
'process.stdout.write("cb");',
156+
);
153157
const stdout = await new Promise<string>((resolve, reject) => {
154158
mod.execFile(script, (err, out) =>
155159
err ? reject(new Error(err.message)) : resolve(out),
156160
);
157161
});
158-
expect(stdout).toBe("ok");
162+
expect(stdout).toBe("cb");
159163
});
160164

161165
it("does not touch spawn", () => {

test/utils/platform.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import os from "node:os";
33
import path from "node:path";
44
import { expect } from "vitest";
55

6-
import type { ChildProcess } from "node:child_process";
6+
import type * as cp from "node:child_process";
77

88
export function isWindows(): boolean {
99
return os.platform() === "win32";
@@ -79,23 +79,26 @@ function prepend(file: string, rest: unknown[]): [string, ...unknown[]] {
7979
* });
8080
* ```
8181
*/
82-
type ChildProcessModule = typeof import("node:child_process");
83-
type Callable = (...args: unknown[]) => unknown;
82+
export function shimExecFile<
83+
M extends { execFile: (...args: never[]) => unknown },
84+
>(mod: M): M {
85+
const { execFile: original } = mod;
8486

85-
export function shimExecFile(mod: ChildProcessModule): ChildProcessModule {
86-
const { execFile: original, ...rest } = mod;
87-
88-
const execFile = (file: string, ...args: unknown[]): ChildProcess =>
89-
(original as unknown as Callable)(...prepend(file, args)) as ChildProcess;
87+
function execFile(file: string, ...rest: unknown[]): cp.ChildProcess {
88+
return Reflect.apply(original, undefined, prepend(file, rest));
89+
}
9090

9191
const sym = Symbol.for("nodejs.util.promisify.custom");
92-
const originalCustom = (original as unknown as Record<symbol, Callable>)[sym];
93-
(execFile as unknown as Record<symbol, unknown>)[sym] = (
94-
file: string,
95-
...args: unknown[]
96-
): unknown => originalCustom(...prepend(file, args));
92+
const customPromisify = Reflect.get(original, sym) as
93+
| ((...args: unknown[]) => unknown)
94+
| undefined;
95+
if (customPromisify) {
96+
Reflect.set(execFile, sym, (file: string, ...rest: unknown[]) =>
97+
Reflect.apply(customPromisify, undefined, prepend(file, rest)),
98+
);
99+
}
97100

98-
return { ...rest, execFile } as ChildProcessModule;
101+
return Object.assign({}, mod, { execFile });
99102
}
100103

101104
export function expectPathsEqual(actual: string, expected: string) {

0 commit comments

Comments
 (0)