summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/context/file
diff options
context:
space:
mode:
authorLuke Parker <[email protected]>2026-02-25 00:03:15 +1000
committerGitHub <[email protected]>2026-02-25 00:03:15 +1000
commit082f0cc12734ccc961797ab9a63dd88a2ce3eed5 (patch)
treeb3f3471be01ba08942c67fd4542874605bd44e2f /packages/app/src/context/file
parent2cee947671fa373098db308b173c859cada0b108 (diff)
downloadopencode-082f0cc12734ccc961797ab9a63dd88a2ce3eed5.tar.gz
opencode-082f0cc12734ccc961797ab9a63dd88a2ce3eed5.zip
fix(app): preserve native path separators in file path helpers (#14912)
Diffstat (limited to 'packages/app/src/context/file')
-rw-r--r--packages/app/src/context/file/path.test.ts4
-rw-r--r--packages/app/src/context/file/path.ts24
2 files changed, 13 insertions, 15 deletions
diff --git a/packages/app/src/context/file/path.test.ts b/packages/app/src/context/file/path.test.ts
index 7eb5e8b2a..feef6d466 100644
--- a/packages/app/src/context/file/path.test.ts
+++ b/packages/app/src/context/file/path.test.ts
@@ -15,10 +15,10 @@ describe("file path helpers", () => {
test("normalizes Windows absolute paths with mixed separators", () => {
const path = createPathHelpers(() => "C:\\repo")
- expect(path.normalize("C:\\repo\\src\\app.ts")).toBe("src/app.ts")
+ expect(path.normalize("C:\\repo\\src\\app.ts")).toBe("src\\app.ts")
expect(path.normalize("C:/repo/src/app.ts")).toBe("src/app.ts")
expect(path.normalize("file://C:/repo/src/app.ts")).toBe("src/app.ts")
- expect(path.normalize("c:\\repo\\src\\app.ts")).toBe("src/app.ts")
+ expect(path.normalize("c:\\repo\\src\\app.ts")).toBe("src\\app.ts")
})
test("keeps query/hash stripping behavior stable", () => {
diff --git a/packages/app/src/context/file/path.ts b/packages/app/src/context/file/path.ts
index 72c058aec..53f072b6c 100644
--- a/packages/app/src/context/file/path.ts
+++ b/packages/app/src/context/file/path.ts
@@ -103,32 +103,30 @@ export function encodeFilePath(filepath: string): string {
export function createPathHelpers(scope: () => string) {
const normalize = (input: string) => {
- const root = scope().replace(/\\/g, "/")
+ const root = scope()
- let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input)))).replace(/\\/g, "/")
+ let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input))))
- // Remove initial root prefix, if it's a complete match or followed by /
- // (don't want /foo/bar to root of /f).
- // For Windows paths, also check for case-insensitive match.
- const windows = /^[A-Za-z]:/.test(root)
- const canonRoot = windows ? root.toLowerCase() : root
- const canonPath = windows ? path.toLowerCase() : path
+ // Separator-agnostic prefix stripping for Cygwin/native Windows compatibility
+ // Only case-insensitive on Windows (drive letter or UNC paths)
+ const windows = /^[A-Za-z]:/.test(root) || root.startsWith("\\\\")
+ const canonRoot = windows ? root.replace(/\\/g, "/").toLowerCase() : root.replace(/\\/g, "/")
+ const canonPath = windows ? path.replace(/\\/g, "/").toLowerCase() : path.replace(/\\/g, "/")
if (
canonPath.startsWith(canonRoot) &&
- (canonRoot.endsWith("/") || canonPath === canonRoot || canonPath.startsWith(canonRoot + "/"))
+ (canonRoot.endsWith("/") || canonPath === canonRoot || canonPath[canonRoot.length] === "/")
) {
- // If we match canonRoot + "/", the slash will be removed below.
+ // Slice from original path to preserve native separators
path = path.slice(root.length)
}
- if (path.startsWith("./")) {
+ if (path.startsWith("./") || path.startsWith(".\\")) {
path = path.slice(2)
}
- if (path.startsWith("/")) {
+ if (path.startsWith("/") || path.startsWith("\\")) {
path = path.slice(1)
}
-
return path
}