Skip to content

Commit fe29ec7

Browse files
LHMQ878cursoragent
andcommitted
fix(core): normalize file watcher paths to forward slashes
Parcel watcher emits backslash paths on Windows while the rest of the codebase compares paths with forward slashes. Normalize watcher events on win32 before publishing. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent fe82a1b commit fe29ec7

2 files changed

Lines changed: 17 additions & 4 deletions

File tree

packages/core/src/filesystem/watcher.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ function getBackend() {
4141
if (process.platform === "linux") return "inotify"
4242
}
4343

44+
export function normalizeEventPath(file: string): string {
45+
return process.platform === "win32" ? file.replaceAll("\\", "/") : file
46+
}
47+
4448
function protecteds(dir: string) {
4549
return Protected.paths().filter((item) => {
4650
const relative = path.relative(dir, item)
@@ -85,9 +89,10 @@ const layer = Layer.effect(
8589

8690
const callback: ParcelWatcher.SubscribeCallback = (_error, updates) => {
8791
for (const update of updates) {
88-
if (update.type === "create") runFork(events.publish(Event.Updated, { file: update.path, event: "add" }))
89-
if (update.type === "update") runFork(events.publish(Event.Updated, { file: update.path, event: "change" }))
90-
if (update.type === "delete") runFork(events.publish(Event.Updated, { file: update.path, event: "unlink" }))
92+
const file = normalizeEventPath(update.path)
93+
if (update.type === "create") runFork(events.publish(Event.Updated, { file, event: "add" }))
94+
if (update.type === "update") runFork(events.publish(Event.Updated, { file, event: "change" }))
95+
if (update.type === "delete") runFork(events.publish(Event.Updated, { file, event: "unlink" }))
9196
}
9297
}
9398

packages/core/test/filesystem/watcher.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { $ } from "bun"
2-
import { describe, expect } from "bun:test"
2+
import { describe, expect, test } from "bun:test"
33
import fs from "fs/promises"
44
import path from "path"
55
import { ConfigProvider, Deferred, Duration, Effect, Fiber, Layer, Option, Stream } from "effect"
@@ -17,6 +17,14 @@ import { testEffect } from "../lib/effect"
1717

1818
const describeWatcher = Watcher.hasNativeBinding() && !process.env.CI ? describe : describe.skip
1919

20+
test("normalizeEventPath converts backslashes on Windows", () => {
21+
if (process.platform !== "win32") {
22+
expect(Watcher.normalizeEventPath("foo/bar")).toBe("foo/bar")
23+
return
24+
}
25+
expect(Watcher.normalizeEventPath("foo\\bar\\baz.txt")).toBe("foo/bar/baz.txt")
26+
})
27+
2028
type WatcherEvent = { file: string; event: "add" | "change" | "unlink" }
2129

2230
const it = testEffect(AppNodeBuilder.build(LayerNode.group([FSUtil.node, EventV2.node])))

0 commit comments

Comments
 (0)