diff --git a/packages/plugin-vite/src/mod.ts b/packages/plugin-vite/src/mod.ts index cd411cacfdc..c49c244526c 100644 --- a/packages/plugin-vite/src/mod.ts +++ b/packages/plugin-vite/src/mod.ts @@ -91,6 +91,23 @@ export function fresh(config?: FreshViteConfig): Plugin[] { isDev = env.command === "serve"; return { + server: { + watch: { + // Ignore temp files, editor swap files, and Vite timestamp + // files. On Linux, these short-lived files can trigger a + // watchFs race condition in Deno where the watcher tries to + // open a file that has already been deleted, crashing the + // dev server with ENOENT. + ignored: [ + "**/*.tmp.*", + "**/*.timestamp-*", + "**/*~", + "**/.#*", + "**/*.swp", + "**/*.swo", + ], + }, + }, esbuild: { jsx: "automatic", jsxImportSource: "preact", diff --git a/packages/plugin-vite/tests/config_test.ts b/packages/plugin-vite/tests/config_test.ts new file mode 100644 index 00000000000..cd650105de8 --- /dev/null +++ b/packages/plugin-vite/tests/config_test.ts @@ -0,0 +1,26 @@ +import { expect } from "@std/expect"; +import { fresh } from "../src/mod.ts"; +import type { Plugin } from "vite"; + +Deno.test("fresh plugin - sets server.watch.ignored patterns", () => { + const plugins = fresh() as Plugin[]; + const freshPlugin = plugins.find((p) => p.name === "fresh"); + expect(freshPlugin).toBeDefined(); + + // Call the config hook as Vite would during dev + // deno-lint-ignore no-explicit-any + const configFn = freshPlugin!.config as any; + const result = configFn({}, { command: "serve" }); + + const ignored = result?.server?.watch?.ignored; + expect(ignored).toBeDefined(); + expect(Array.isArray(ignored)).toBe(true); + + // Should ignore temp files, editor swap files, and Vite timestamp files + expect(ignored).toContain("**/*.tmp.*"); + expect(ignored).toContain("**/*.timestamp-*"); + expect(ignored).toContain("**/*.swp"); + expect(ignored).toContain("**/*.swo"); + expect(ignored).toContain("**/*~"); + expect(ignored).toContain("**/.#*"); +});