Skip to content

Commit 8db0637

Browse files
kraenhansenclaude
andcommitted
Resolve Xcode app project resiliently in workspaces
react-native-test-app 5.x generates the app's ReactTestApp.xcodeproj under the nearest node_modules, which in a workspace is the app-local node_modules (apps/test-app/node_modules/.generated), not the hoisted root. The workspace can also accumulate stale references to a project under a different node_modules. findXcodeProject took the first fileRef unconditionally, which could be the stale (non-existent) reference or the Pods project. Resolve every app project reference and pick the first whose project.pbxproj exists on disk, ignoring Pods.xcodeproj. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3fef88b commit 8db0637

1 file changed

Lines changed: 26 additions & 11 deletions

File tree

packages/host/src/node/cli/xcode-helpers.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,9 @@ export async function findXcodeWorkspace(fromPath: string) {
6868
throw new Error(`No Xcode workspace found in '${fromPath}'`);
6969
}
7070

71-
export async function findXcodeProject(fromPath: string) {
72-
// Read the workspace contents to find the first project
73-
const workspacePath = await findXcodeWorkspace(fromPath);
74-
const workspace = await readXcodeWorkspace(workspacePath);
75-
// Resolve the first project location to an absolute path
76-
assert(
77-
workspace.fileRefs.length > 0,
78-
"Expected at least one project in the workspace",
79-
);
80-
const [firstProject] = workspace.fileRefs;
71+
function resolveWorkspaceFileRef(location: string, workspacePath: string) {
8172
// Extract the path from the scheme (using a regex)
82-
const match = firstProject.location.match(/^([^:]*):(.*)$/);
73+
const match = location.match(/^([^:]*):(.*)$/);
8374
assert(match, "Expected a project path in the workspace");
8475
const [, scheme, projectPath] = match;
8576
assert(scheme, "Expected a scheme in the fileRef location");
@@ -93,6 +84,30 @@ export async function findXcodeProject(fromPath: string) {
9384
}
9485
}
9586

87+
export async function findXcodeProject(fromPath: string) {
88+
const workspacePath = await findXcodeWorkspace(fromPath);
89+
const workspace = await readXcodeWorkspace(workspacePath);
90+
assert(
91+
workspace.fileRefs.length > 0,
92+
"Expected at least one project in the workspace",
93+
);
94+
// The workspace references the Pods project alongside the app project, and in
95+
// a monorepo it can accumulate stale references to app projects generated
96+
// under a different node_modules. Pick the first referenced app project that
97+
// actually exists on disk (ignoring the Pods project).
98+
const appProjectPaths = workspace.fileRefs
99+
.map(({ location }) => resolveWorkspaceFileRef(location, workspacePath))
100+
.filter((projectPath) => path.basename(projectPath) !== "Pods.xcodeproj");
101+
const existingProjectPath = appProjectPaths.find((projectPath) =>
102+
fs.existsSync(path.join(projectPath, "project.pbxproj")),
103+
);
104+
assert(
105+
existingProjectPath,
106+
`Expected one of the workspace's projects to exist: ${appProjectPaths.join(", ")}`,
107+
);
108+
return existingProjectPath;
109+
}
110+
96111
export type ExpectedFrameworkSlice = {
97112
platform: string;
98113
platformVariant?: string;

0 commit comments

Comments
 (0)