From 42189e1bf9be6dc02334494458dde561e343add4 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 15 Apr 2026 09:06:38 -0500 Subject: [PATCH] Ignore UI test files in Vite dev watch (cherry picked from commit c3d4e78e6f2038fd17cdd3f1cebb64a66489272b) --- ui/src/lib/vite-watch.test.ts | 29 +++++++++++++++++++++++++++++ ui/src/lib/vite-watch.ts | 29 +++++++++++++++++++++++++++++ ui/vite.config.ts | 4 ++-- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 ui/src/lib/vite-watch.test.ts create mode 100644 ui/src/lib/vite-watch.ts diff --git a/ui/src/lib/vite-watch.test.ts b/ui/src/lib/vite-watch.test.ts new file mode 100644 index 00000000000..c4b456b62a0 --- /dev/null +++ b/ui/src/lib/vite-watch.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from "vitest"; +import { createUiDevWatchOptions, shouldIgnoreUiDevWatchPath } from "./vite-watch"; + +describe("shouldIgnoreUiDevWatchPath", () => { + it("ignores test-only files and folders", () => { + expect(shouldIgnoreUiDevWatchPath("/repo/ui/src/components/IssuesList.test.tsx")).toBe(true); + expect(shouldIgnoreUiDevWatchPath("/repo/ui/src/lib/issue-tree.spec.ts")).toBe(true); + expect(shouldIgnoreUiDevWatchPath("/repo/ui/src/__tests__/helpers.ts")).toBe(true); + expect(shouldIgnoreUiDevWatchPath("/repo/ui/tests/helpers.ts")).toBe(true); + }); + + it("keeps runtime source files watchable", () => { + expect(shouldIgnoreUiDevWatchPath("/repo/ui/src/components/IssuesList.tsx")).toBe(false); + expect(shouldIgnoreUiDevWatchPath("/repo/ui/src/pages/IssueDetail.tsx")).toBe(false); + }); +}); + +describe("createUiDevWatchOptions", () => { + it("preserves the WSL /mnt polling fallback", () => { + expect(createUiDevWatchOptions("/mnt/c/paperclip")).toMatchObject({ + usePolling: true, + interval: 1000, + }); + }); + + it("always includes the ignored-path predicate", () => { + expect(createUiDevWatchOptions("/Users/dotta/paperclip")).toHaveProperty("ignored"); + }); +}); diff --git a/ui/src/lib/vite-watch.ts b/ui/src/lib/vite-watch.ts new file mode 100644 index 00000000000..f5792a2e191 --- /dev/null +++ b/ui/src/lib/vite-watch.ts @@ -0,0 +1,29 @@ +const TEST_DIRECTORY_NAMES = new Set([ + "__tests__", + "_tests", + "test", + "tests", +]); + +const TEST_FILE_BASENAME_RE = /\.(test|spec)\.[^/]+$/i; + +export function shouldIgnoreUiDevWatchPath(watchedPath: string): boolean { + const normalizedPath = String(watchedPath).replaceAll("\\", "/"); + if (normalizedPath.length === 0) return false; + + const segments = normalizedPath.split("/"); + const basename = segments.at(-1) ?? normalizedPath; + + return segments.some((segment) => TEST_DIRECTORY_NAMES.has(segment)) + || TEST_FILE_BASENAME_RE.test(basename); +} + +export function createUiDevWatchOptions(currentWorkingDirectory: string) { + return { + ignored: shouldIgnoreUiDevWatchPath, + // WSL2 /mnt/ drives don't support inotify — fall back to polling so HMR works. + ...(currentWorkingDirectory.startsWith("/mnt/") + ? { usePolling: true, interval: 1000 } + : {}), + }; +} diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 395eebb9d3f..468c34e91d5 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -2,6 +2,7 @@ import path from "path"; import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; +import { createUiDevWatchOptions } from "./src/lib/vite-watch"; export default defineConfig({ plugins: [react(), tailwindcss()], @@ -13,8 +14,7 @@ export default defineConfig({ }, server: { port: 5173, - // WSL2 /mnt/ drives don't support inotify — fall back to polling so HMR works - watch: process.cwd().startsWith("/mnt/") ? { usePolling: true, interval: 1000 } : undefined, + watch: createUiDevWatchOptions(process.cwd()), proxy: { "/api": { target: "http://localhost:3100",