Skip to content

Commit 8f66e41

Browse files
committed
refactor(path): drop Windows tilde branch and consolidate helpers
- Remove dead `~\` expansion branch and its test; only POSIX is supported. - Add overload so resolvePathFromCwd accepts string | undefined, dropping the ternary at app-path-resolver call sites. - Replace duplicate tilde handling in snapshot-tests/output-parsers with the shared expandHomePrefix helper. - Add defensive empty-input guard and clarified doc comments to expandHomePrefix; document that ~userName prefixes are not expanded. - Cover whitespace, multi-byte, and undefined cases in path.test.ts. - Note absolute-path normalization in the CHANGELOG fix entry.
1 parent c83368a commit 8f66e41

5 files changed

Lines changed: 39 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
### Fixed
1717

18-
- Expanded leading `~` (and `~/` or `~\` on Windows) prefixes in configured `derivedDataPath`, `projectPath`, `workspacePath`, `axePath`, and the iOS/macOS template paths so values like `~/.foo/derivedData` resolve under the user's home directory instead of creating a literal `~` directory under the project root ([#283](https://github.com/getsentry/XcodeBuildMCP/issues/283), supersedes [#301](https://github.com/getsentry/XcodeBuildMCP/pull/301) by [@trmquang93](https://github.com/trmquang93)).
18+
- Expanded leading `~` and `~/` prefixes in configured `derivedDataPath`, `projectPath`, `workspacePath`, `axePath`, and the iOS/macOS template paths so values like `~/.foo/derivedData` resolve under the user's home directory instead of creating a literal `~` directory under the project root. As part of this change, configured absolute paths are now lexically normalized (e.g. `/a/b/../c` collapses to `/a/c`) before being passed to `xcodebuild` ([#283](https://github.com/getsentry/XcodeBuildMCP/issues/283), supersedes [#301](https://github.com/getsentry/XcodeBuildMCP/pull/301) by [@trmquang93](https://github.com/trmquang93)).
1919

2020
## [2.3.2]
2121

src/snapshot-tests/output-parsers.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import os from 'node:os';
1+
import { expandHomePrefix } from '../utils/path.ts';
22

33
export interface SnapshotSimulatorEntry {
44
name: string;
@@ -7,10 +7,7 @@ export interface SnapshotSimulatorEntry {
77
}
88

99
export function expandSnapshotPath(pathValue: string): string {
10-
if (pathValue.startsWith('~/')) {
11-
return `${os.homedir()}${pathValue.slice(1)}`;
12-
}
13-
return pathValue;
10+
return expandHomePrefix(pathValue);
1411
}
1512

1613
export function extractAppPathFromSnapshotOutput(output: string): string {

src/utils/__tests__/path.test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ describe('expandHomePrefix', () => {
1212
expect(expandHomePrefix('~/foo/bar')).toBe(path.join(homedir(), 'foo/bar'));
1313
});
1414

15-
it('expands a leading ~\\ on Windows-style separators', () => {
16-
expect(expandHomePrefix('~\\foo\\bar')).toBe(path.join(homedir(), 'foo\\bar'));
17-
});
18-
1915
it('returns absolute paths unchanged', () => {
2016
expect(expandHomePrefix('/absolute/path')).toBe('/absolute/path');
2117
});
@@ -32,6 +28,14 @@ describe('expandHomePrefix', () => {
3228
expect(expandHomePrefix('foo/~/bar')).toBe('foo/~/bar');
3329
});
3430

31+
it('does not expand a leading ~ followed by whitespace', () => {
32+
expect(expandHomePrefix(' ~/foo')).toBe(' ~/foo');
33+
});
34+
35+
it('preserves multi-byte characters in the expanded segment', () => {
36+
expect(expandHomePrefix('~/日本語/файл')).toBe(path.join(homedir(), '日本語/файл'));
37+
});
38+
3539
it('returns an empty string unchanged', () => {
3640
expect(expandHomePrefix('')).toBe('');
3741
});
@@ -75,4 +79,8 @@ describe('resolvePathFromCwd', () => {
7579
it('normalizes interior traversal segments in absolute paths', () => {
7680
expect(resolvePathFromCwd('/a/b/../c')).toBe('/a/c');
7781
});
82+
83+
it('returns undefined when pathValue is undefined', () => {
84+
expect(resolvePathFromCwd(undefined)).toBeUndefined();
85+
});
7886
});

src/utils/app-path-resolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ export async function resolveAppPathFromBuildSettings(
4646
): Promise<string> {
4747
const command = ['xcodebuild', '-showBuildSettings'];
4848

49-
const workspacePath = params.workspacePath ? resolvePathFromCwd(params.workspacePath) : undefined;
50-
const projectPath = params.projectPath ? resolvePathFromCwd(params.projectPath) : undefined;
49+
const workspacePath = resolvePathFromCwd(params.workspacePath);
50+
const projectPath = resolvePathFromCwd(params.projectPath);
5151
const derivedDataPath = resolveEffectiveDerivedDataPath(params.derivedDataPath);
5252

5353
let projectDir: string | undefined;

src/utils/path.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,41 @@ import path from 'node:path';
22
import { homedir } from 'node:os';
33

44
/**
5-
* Expand a leading ~ or ~/ (or ~\ on Windows) prefix to the user's home directory.
6-
* Returns the path unchanged if it does not start with ~ or starts with ~userName.
5+
* Expand a leading `~` or `~/` prefix to the user's home directory.
6+
* Returns the path unchanged if it does not begin with `~` or `~/`.
7+
* Shell-style `~userName` prefixes (e.g. `~bob/foo`) are not expanded
8+
* and will be treated as literal path segments by `resolvePathFromCwd`.
79
*/
810
export function expandHomePrefix(inputPath: string): string {
11+
if (!inputPath) {
12+
return inputPath;
13+
}
14+
915
if (inputPath === '~') {
1016
return homedir();
1117
}
1218

13-
if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) {
19+
if (inputPath.startsWith('~/')) {
1420
return path.join(homedir(), inputPath.slice(2));
1521
}
1622

1723
return inputPath;
1824
}
1925

2026
/**
21-
* Resolve a user-supplied path: expand ~ then resolve against `cwd`
22-
* (defaults to process.cwd()). Always returns a normalized absolute path —
23-
* traversal segments like `/foo/..` collapse to `/`.
27+
* Resolve a user-supplied path: expand `~` then resolve against `cwd`
28+
* (defaults to `process.cwd()`). Always returns a normalized absolute path —
29+
* traversal segments like `/foo/..` collapse to `/`. Returns `undefined`
30+
* when `pathValue` is `undefined`.
2431
*/
25-
export function resolvePathFromCwd(pathValue: string, cwd: string = process.cwd()): string {
32+
export function resolvePathFromCwd(pathValue: string, cwd?: string): string;
33+
export function resolvePathFromCwd(pathValue: string | undefined, cwd?: string): string | undefined;
34+
export function resolvePathFromCwd(
35+
pathValue: string | undefined,
36+
cwd: string = process.cwd(),
37+
): string | undefined {
38+
if (pathValue === undefined) {
39+
return undefined;
40+
}
2641
return path.resolve(cwd, expandHomePrefix(pathValue));
2742
}

0 commit comments

Comments
 (0)