Skip to content

Commit 05a293a

Browse files
committed
fix(ui): make ui build work on Windows
1 parent 52a0aa0 commit 05a293a

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

scripts/ui.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ export function shouldUseShellForCommand(cmd, platform = process.platform) {
6161
return WINDOWS_SHELL_EXTENSIONS.has(extension);
6262
}
6363

64+
export function quoteWindowsShellCommand(cmd, platform = process.platform) {
65+
if (!shouldUseShellForCommand(cmd, platform) || !/\s/.test(cmd)) {
66+
return cmd;
67+
}
68+
if (cmd.startsWith('"') && cmd.endsWith('"')) {
69+
return cmd;
70+
}
71+
return `"${cmd}"`;
72+
}
73+
6474
export function assertSafeWindowsShellArgs(args, platform = process.platform) {
6575
if (platform !== "win32") {
6676
return;
@@ -91,8 +101,9 @@ function createSpawnOptions(cmd, args, envOverride) {
91101

92102
function run(cmd, args) {
93103
let child;
104+
const actualCmd = quoteWindowsShellCommand(cmd);
94105
try {
95-
child = spawn(cmd, args, createSpawnOptions(cmd, args));
106+
child = spawn(actualCmd, args, createSpawnOptions(cmd, args));
96107
} catch (err) {
97108
console.error(`Failed to launch ${cmd}:`, err);
98109
process.exit(1);
@@ -112,8 +123,9 @@ function run(cmd, args) {
112123

113124
function runSync(cmd, args, envOverride) {
114125
let result;
126+
const actualCmd = quoteWindowsShellCommand(cmd);
115127
try {
116-
result = spawnSync(cmd, args, createSpawnOptions(cmd, args, envOverride));
128+
result = spawnSync(actualCmd, args, createSpawnOptions(cmd, args, envOverride));
117129
} catch (err) {
118130
console.error(`Failed to launch ${cmd}:`, err);
119131
process.exit(1);
@@ -184,10 +196,7 @@ export function main(argv = process.argv.slice(2)) {
184196
}
185197

186198
if (!depsInstalled(action === "test" ? "test" : "build")) {
187-
const installEnv =
188-
action === "build" ? { ...process.env, NODE_ENV: "production" } : process.env;
189-
const installArgs = action === "build" ? ["install", "--prod"] : ["install"];
190-
runSync(runner.cmd, installArgs, installEnv);
199+
runSync(runner.cmd, ["install"]);
191200
}
192201

193202
run(runner.cmd, ["run", script, ...rest]);

test/scripts/ui.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, expect, it } from "vitest";
2-
import { assertSafeWindowsShellArgs, shouldUseShellForCommand } from "../../scripts/ui.js";
2+
import {
3+
assertSafeWindowsShellArgs,
4+
quoteWindowsShellCommand,
5+
shouldUseShellForCommand,
6+
} from "../../scripts/ui.js";
37

48
describe("scripts/ui windows spawn behavior", () => {
59
it("enables shell for Windows command launchers that require cmd.exe", () => {
@@ -14,6 +18,16 @@ describe("scripts/ui windows spawn behavior", () => {
1418
expect(shouldUseShellForCommand("/usr/local/bin/pnpm", "linux")).toBe(false);
1519
});
1620

21+
it("quotes Windows shell launcher paths that contain spaces", () => {
22+
expect(quoteWindowsShellCommand("C:\\Program Files\\pnpm\\pnpm.cmd", "win32")).toBe(
23+
'"C:\\Program Files\\pnpm\\pnpm.cmd"',
24+
);
25+
expect(quoteWindowsShellCommand("C:\\tools\\pnpm.cmd", "win32")).toBe("C:\\tools\\pnpm.cmd");
26+
expect(quoteWindowsShellCommand("C:\\Program Files\\nodejs\\node.exe", "win32")).toBe(
27+
"C:\\Program Files\\nodejs\\node.exe",
28+
);
29+
});
30+
1731
it("allows safe forwarded args when shell mode is required on Windows", () => {
1832
expect(() =>
1933
assertSafeWindowsShellArgs(["run", "build", "--filter", "@openclaw/ui"], "win32"),

0 commit comments

Comments
 (0)