Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion accessibility/keyboardui.js
Original file line number Diff line number Diff line change
Expand Up @@ -963,4 +963,4 @@ if (document.getElementById('info-panel-tabs')) {
ShortcutsPanel.init();
}

export { InfoPanel, ShortcutsPanel, GizmoMenuManager };
export { InfoPanel, ShortcutsPanel, GizmoMenuManager, AreaManager };
39 changes: 26 additions & 13 deletions scripts/run-api-tests.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

import { chromium } from "playwright";
import { spawn } from "child_process";
import { spawn, execFileSync } from "child_process";
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
Expand Down Expand Up @@ -326,22 +326,28 @@ async function startServer() {
const outputBuffer = [];
const errorBuffer = [];

// Run npm directly without a shell. This avoids Node DEP0190
// (deprecation of `shell: true` with an args array). On macOS/Linux/CI,
// `npm` resolves from PATH; on Windows the launcher is `npm.cmd`.
// Alternative if you need shell features (e.g. on Windows): pass the
// command as a single string with `shell: true` and no args array, e.g.
// spawn("npm run dev", { cwd: ..., stdio: "pipe", shell: true, env: ... })
// On Windows, `npm` is a .cmd launcher: spawning "npm" directly (no
// shell) throws ENOENT, and spawning "npm.cmd" directly (no shell)
// throws EINVAL — recent Node versions require shell:true to launch
// .cmd/.bat files at all. That's Windows-only; on POSIX, `npm` resolves
// and execs directly from PATH, so keep the original argv-array spawn
// there rather than routing everything through `sh -c` unconditionally.
// Using a single command string with shell:true (rather than shell:true
// with an args array) avoids the Node DEP0190 deprecation.
// `detached: true` puts npm and its child vite/esbuild processes in their
// own process group so cleanup() can kill the whole group. Without this,
// server.kill() only signals npm, leaving an orphaned vite holding port
// 5173 that the next run wrongly "reuses".
server = spawn("npm", ["run", "dev"], {
const spawnOptions = {
cwd: path.resolve(__dirname, ".."),
stdio: "pipe",
detached: true,
env: { ...process.env },
});
};
server =
process.platform === "win32"
? spawn("npm run dev", { ...spawnOptions, shell: true })
: spawn("npm", ["run", "dev"], spawnOptions);

server.stdout.on("data", (data) => {
const output = data.toString();
Expand Down Expand Up @@ -1039,16 +1045,23 @@ async function runTests(suiteId = "all") {
function cleanup() {
if (server) {
console.log("\n🛑 Stopping development server...");
// Kill the whole process group (npm + child vite/esbuild). server.kill()
// alone only signals npm and leaves vite orphaned on port 5173.
// Kill the whole tree (npm + child vite/esbuild). server.kill() alone
// only signals the immediate child and leaves vite orphaned on port 5173.
// On Windows, negative-pid process-group kill is a no-op (Windows PIDs
// are never negative) and TerminateProcess doesn't cascade to children,
// so `taskkill /T` is used instead to walk the whole descendant tree.
try {
if (server.pid) {
if (server.pid && process.platform === "win32") {
execFileSync("taskkill", ["/pid", String(server.pid), "/T", "/F"], {
stdio: "ignore",
});
} else if (server.pid) {
process.kill(-server.pid, "SIGTERM");
} else {
server.kill("SIGTERM");
}
} catch {
// Group may already be gone, or platform lacks process groups; fall back.
// Group/tree may already be gone; fall back to signalling the child.
server.kill("SIGTERM");
}
}
Expand Down
Loading
Loading