Skip to content

Commit d09b3e2

Browse files
committed
fix(cli): path to uri and vice versa conversion
1 parent 769c2ad commit d09b3e2

File tree

2 files changed

+263
-2
lines changed

2 files changed

+263
-2
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import { describe, it, expect } from "vitest";
2+
import { pathToUri, uriToPath, slugToUri, uriToSlug } from "./uriUtils";
3+
import { join, resolve, normalize } from "path";
4+
import { platform } from "os";
5+
6+
describe("uriUtils", () => {
7+
describe("pathToUri", () => {
8+
it("should convert Unix-style absolute paths to file URIs", () => {
9+
const unixPath = "/home/user/documents/file.txt";
10+
const result = pathToUri(unixPath);
11+
expect(result).toMatch(/^file:\/\/\//);
12+
expect(result).toContain("file.txt");
13+
});
14+
15+
it("should convert Unix-style relative paths to file URIs", () => {
16+
const relativePath = "./documents/file.txt";
17+
const result = pathToUri(relativePath);
18+
expect(result).toMatch(/^file:\/\/\//);
19+
expect(result).toContain("file.txt");
20+
});
21+
22+
it("should handle Windows-style paths with drive letters", () => {
23+
const windowsPath = "C:\\Users\\user\\Documents\\file.txt";
24+
const result = pathToUri(windowsPath);
25+
expect(result).toMatch(/^file:\/\/\//);
26+
expect(result).toContain("file.txt");
27+
// Windows paths should be converted to forward slashes in URI
28+
if (platform() === "win32") {
29+
expect(result).toContain("C:");
30+
}
31+
});
32+
33+
it("should handle Windows UNC paths", () => {
34+
const uncPath = "\\\\server\\share\\folder\\file.txt";
35+
const result = pathToUri(uncPath);
36+
expect(result).toMatch(/^file:\/\/\//);
37+
expect(result).toContain("file.txt");
38+
});
39+
40+
it("should handle paths with special characters", () => {
41+
const pathWithSpaces = "/home/user/my documents/file with spaces.txt";
42+
const result = pathToUri(pathWithSpaces);
43+
expect(result).toMatch(/^file:\/\/\//);
44+
// Spaces should be properly encoded
45+
expect(result).toContain("file%20with%20spaces.txt");
46+
});
47+
48+
it("should handle paths with Unicode characters", () => {
49+
const unicodePath = "/home/user/文档/файл.txt";
50+
const result = pathToUri(unicodePath);
51+
expect(result).toMatch(/^file:\/\/\//);
52+
// Should handle Unicode properly
53+
expect(result).toContain("%E6%96%87%E6%A1%A3"); // 文档 encoded
54+
});
55+
56+
it("should normalize mixed path separators", () => {
57+
const mixedPath = "/home/user\\documents/file.txt";
58+
const result = pathToUri(mixedPath);
59+
expect(result).toMatch(/^file:\/\/\//);
60+
expect(result).toContain("file.txt");
61+
});
62+
63+
it("should handle empty string", () => {
64+
const result = pathToUri("");
65+
expect(result).toMatch(/^file:\/\/\//);
66+
});
67+
68+
it("should handle current directory", () => {
69+
const result = pathToUri(".");
70+
expect(result).toMatch(/^file:\/\/\//);
71+
});
72+
});
73+
74+
describe("uriToPath", () => {
75+
it("should convert file URIs to Unix-style paths", () => {
76+
const uri = "file:///home/user/documents/file.txt";
77+
const result = uriToPath(uri);
78+
expect(result).toBeTruthy();
79+
if (platform() === "win32") {
80+
// On Windows, this might be converted differently
81+
expect(result).toContain("file.txt");
82+
} else {
83+
expect(result).toBe("/home/user/documents/file.txt");
84+
}
85+
});
86+
87+
it("should convert Windows file URIs to Windows paths", () => {
88+
const uri = "file:///C:/Users/user/Documents/file.txt";
89+
const result = uriToPath(uri);
90+
expect(result).toBeTruthy();
91+
expect(result).toContain("file.txt");
92+
if (platform() === "win32") {
93+
expect(result).toContain("C:");
94+
expect(result).toContain("\\\\"); // Windows path separators
95+
}
96+
});
97+
98+
it("should handle URIs with encoded special characters", () => {
99+
const uri = "file:///home/user/my%20documents/file%20with%20spaces.txt";
100+
const result = uriToPath(uri);
101+
expect(result).toBeTruthy();
102+
expect(result).toContain("my documents");
103+
expect(result).toContain("file with spaces.txt");
104+
});
105+
106+
it("should handle URIs with Unicode characters", () => {
107+
const uri = "file:///home/user/%E6%96%87%E6%A1%A3/%E6%96%87%E4%BB%B6.txt";
108+
const result = uriToPath(uri);
109+
expect(result).toBeTruthy();
110+
expect(result).toContain("文档");
111+
expect(result).toContain("文件.txt");
112+
});
113+
114+
it("should return null for non-file URIs", () => {
115+
const httpUri = "http://example.com/file.txt";
116+
const result = uriToPath(httpUri);
117+
expect(result).toBeNull();
118+
});
119+
120+
it("should return null for malformed URIs", () => {
121+
const malformedUri = "file://invalid-uri";
122+
const result = uriToPath(malformedUri);
123+
expect(result).toBeNull();
124+
});
125+
126+
it("should handle UNC paths on Windows", () => {
127+
const uri = "file://server/share/folder/file.txt";
128+
const result = uriToPath(uri);
129+
if (platform() === "win32") {
130+
expect(result).toBeTruthy();
131+
expect(result).toContain("file.txt");
132+
} else {
133+
// On Unix systems, this might be handled differently or return null
134+
expect(result !== null || result === null).toBe(true);
135+
}
136+
});
137+
138+
it("should return null for empty URI", () => {
139+
const result = uriToPath("");
140+
expect(result).toBeNull();
141+
});
142+
143+
it("should handle root directory URI", () => {
144+
const uri = "file:///";
145+
const result = uriToPath(uri);
146+
expect(result).toBeTruthy();
147+
if (platform() === "win32") {
148+
expect(result).toContain("\\\\");
149+
} else {
150+
expect(result).toBe("/");
151+
}
152+
});
153+
});
154+
155+
describe("round-trip conversion", () => {
156+
const testPaths = [
157+
"/home/user/documents/file.txt", // Unix absolute
158+
"./relative/path/file.txt", // Relative
159+
"../parent/file.txt", // Parent relative
160+
"/tmp/file with spaces.txt", // Spaces
161+
"/home/用户/文档/文件.txt", // Unicode
162+
];
163+
164+
// Add Windows-specific paths if running on Windows
165+
const windowsPaths = [
166+
"C:\\Users\\user\\Documents\\file.txt", // Windows absolute
167+
"D:\\Program Files\\app\\file.exe", // Program Files
168+
"\\\\server\\share\\file.txt", // UNC path
169+
];
170+
171+
const allPaths =
172+
platform() === "win32" ? [...testPaths, ...windowsPaths] : testPaths;
173+
174+
allPaths.forEach((originalPath) => {
175+
it(`should maintain path integrity for: ${originalPath}`, () => {
176+
const uri = pathToUri(originalPath);
177+
const roundTripPath = uriToPath(uri);
178+
179+
expect(roundTripPath).toBeTruthy();
180+
181+
// Normalize both paths for comparison since path separators might differ
182+
const normalizedOriginal = normalize(resolve(originalPath));
183+
const normalizedRoundTrip = normalize(roundTripPath!);
184+
185+
expect(normalizedRoundTrip).toBe(normalizedOriginal);
186+
});
187+
});
188+
});
189+
190+
describe("slugToUri", () => {
191+
it("should convert slug to URI", () => {
192+
const slug = "my-awesome-project";
193+
const result = slugToUri(slug);
194+
expect(result).toBe("slug://my-awesome-project");
195+
});
196+
197+
it("should handle empty slug", () => {
198+
const slug = "";
199+
const result = slugToUri(slug);
200+
expect(result).toBe("slug://");
201+
});
202+
203+
it("should handle slug with special characters", () => {
204+
const slug = "my-project_123";
205+
const result = slugToUri(slug);
206+
expect(result).toBe("slug://my-project_123");
207+
});
208+
});
209+
210+
describe("uriToSlug", () => {
211+
it("should extract slug from URI", () => {
212+
const uri = "slug://my-awesome-project";
213+
const result = uriToSlug(uri);
214+
expect(result).toBe("my-awesome-project");
215+
});
216+
217+
it("should return null for non-slug URIs", () => {
218+
const uri = "file:///path/to/file";
219+
const result = uriToSlug(uri);
220+
expect(result).toBeNull();
221+
});
222+
223+
it("should handle empty slug URI", () => {
224+
const uri = "slug://";
225+
const result = uriToSlug(uri);
226+
expect(result).toBe("");
227+
});
228+
229+
it("should return null for malformed URI", () => {
230+
const uri = "slug:/missing-slash";
231+
const result = uriToSlug(uri);
232+
expect(result).toBeNull();
233+
});
234+
});
235+
236+
describe("slug round-trip conversion", () => {
237+
const testSlugs = [
238+
"simple-slug",
239+
"complex_slug-123",
240+
"",
241+
"a",
242+
"very-long-slug-name-with-many-parts",
243+
];
244+
245+
testSlugs.forEach((originalSlug) => {
246+
it(`should maintain slug integrity for: "${originalSlug}"`, () => {
247+
const uri = slugToUri(originalSlug);
248+
const roundTripSlug = uriToSlug(uri);
249+
expect(roundTripSlug).toBe(originalSlug);
250+
});
251+
});
252+
});
253+
});

extensions/cli/src/auth/uriUtils.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import { normalize } from "path";
2+
import { fileURLToPath, pathToFileURL } from "url";
3+
14
/**
25
* URI utility functions for auth config
36
*/
47

58
export function pathToUri(path: string): string {
6-
return `file://${path}`;
9+
const normalizedPath = normalize(path);
10+
return pathToFileURL(normalizedPath).href;
711
}
812

913
export function slugToUri(slug: string): string {
@@ -14,7 +18,11 @@ export function uriToPath(uri: string): string | null {
1418
if (!uri.startsWith("file://")) {
1519
return null;
1620
}
17-
return uri.slice(7);
21+
try {
22+
return fileURLToPath(uri);
23+
} catch (error) {
24+
return null;
25+
}
1826
}
1927

2028
export function uriToSlug(uri: string): string | null {

0 commit comments

Comments
 (0)