mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add script to find the directory that contains the Xcodeproj (#53669)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53669 ## Context When configuring an app to build with SwiftPM from source, there is a sequence of operations we need to run in order to prepare the project correctly. ## Changed Add a function that given the root of the app and the name of the xcodeproject file, can return the path to the Xcode project file ## Changelog: [Internal] - Reviewed By: cortinico Differential Revision: D81778456 fbshipit-source-id: f7050bcb049d75a5b1cabf340a5b98f4736e60b3
This commit is contained in:
committed by
meta-codesync[bot]
parent
53464e8483
commit
f0725f7802
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @noflow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {findXcodeProjectDirectory} = require('../prepare-app-utils');
|
||||
|
||||
// Mock child_process module
|
||||
jest.mock('child_process');
|
||||
|
||||
describe('findXcodeProjectDirectory', () => {
|
||||
let mockExecSync;
|
||||
|
||||
beforeEach(() => {
|
||||
// Setup mock
|
||||
const childProcess = require('child_process');
|
||||
mockExecSync = childProcess.execSync;
|
||||
|
||||
// Reset all mocks
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should find Xcode project directory successfully', () => {
|
||||
// Setup
|
||||
const appPath = '/path/to/app';
|
||||
const xcodeProjectName = 'MyApp.xcodeproj';
|
||||
const mockResult = '/path/to/app/ios/MyApp.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue(mockResult + '\n');
|
||||
|
||||
// Execute
|
||||
const result = findXcodeProjectDirectory(appPath, xcodeProjectName);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('/path/to/app/ios');
|
||||
expect(mockExecSync).toHaveBeenCalledWith(
|
||||
`find "${appPath}" -name "${xcodeProjectName}" -type d -print`,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
expect(mockExecSync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should find Xcode project in nested subdirectory', () => {
|
||||
// Setup
|
||||
const appPath = '/Users/developer/ReactNativeApp';
|
||||
const xcodeProjectName = 'ReactNativeApp.xcodeproj';
|
||||
const mockResult =
|
||||
'/Users/developer/ReactNativeApp/ios/sub/ReactNativeApp.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue(mockResult + '\n');
|
||||
|
||||
// Execute
|
||||
const result = findXcodeProjectDirectory(appPath, xcodeProjectName);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('/Users/developer/ReactNativeApp/ios/sub');
|
||||
expect(mockExecSync).toHaveBeenCalledWith(
|
||||
`find "${appPath}" -name "${xcodeProjectName}" -type d -print`,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle project found at root level', () => {
|
||||
// Setup
|
||||
const appPath = '/path/to/project';
|
||||
const xcodeProjectName = 'RootProject.xcodeproj';
|
||||
const mockResult = '/path/to/project/RootProject.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue(mockResult + '\n');
|
||||
|
||||
// Execute
|
||||
const result = findXcodeProjectDirectory(appPath, xcodeProjectName);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('/path/to/project');
|
||||
expect(mockExecSync).toHaveBeenCalledWith(
|
||||
`find "${appPath}" -name "${xcodeProjectName}" -type d -print`,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle paths with spaces in directory names', () => {
|
||||
// Setup
|
||||
const appPath = '/path/to/my app';
|
||||
const xcodeProjectName = 'My App.xcodeproj';
|
||||
const mockResult = '/path/to/my app/ios folder/My App.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue(mockResult + '\n');
|
||||
|
||||
// Execute
|
||||
const result = findXcodeProjectDirectory(appPath, xcodeProjectName);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('/path/to/my app/ios folder');
|
||||
expect(mockExecSync).toHaveBeenCalledWith(
|
||||
`find "${appPath}" -name "${xcodeProjectName}" -type d -print`,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when Xcode project is not found', () => {
|
||||
// Setup
|
||||
const appPath = '/path/to/app';
|
||||
const xcodeProjectName = 'NonExistent.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue('');
|
||||
|
||||
// Execute & Assert
|
||||
expect(() => findXcodeProjectDirectory(appPath, xcodeProjectName)).toThrow(
|
||||
`Xcode project 'NonExistent.xcodeproj' not found in '/path/to/app' or its subdirectories`,
|
||||
);
|
||||
|
||||
expect(mockExecSync).toHaveBeenCalledWith(
|
||||
`find "${appPath}" -name "${xcodeProjectName}" -type d -print`,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when find command returns only whitespace', () => {
|
||||
// Setup
|
||||
const appPath = '/path/to/app';
|
||||
const xcodeProjectName = 'Missing.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue(' \n \t ');
|
||||
|
||||
// Execute & Assert
|
||||
expect(() => findXcodeProjectDirectory(appPath, xcodeProjectName)).toThrow(
|
||||
`Xcode project 'Missing.xcodeproj' not found in '/path/to/app' or its subdirectories`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should properly escape quotes in app path', () => {
|
||||
// Setup
|
||||
const appPath = '/path/to/app with "quotes"';
|
||||
const xcodeProjectName = 'MyApp.xcodeproj';
|
||||
const mockResult = '/path/to/app with "quotes"/ios/MyApp.xcodeproj';
|
||||
|
||||
mockExecSync.mockReturnValue(mockResult + '\n');
|
||||
|
||||
// Execute
|
||||
const result = findXcodeProjectDirectory(appPath, xcodeProjectName);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('/path/to/app with "quotes"/ios');
|
||||
expect(mockExecSync).toHaveBeenCalledWith(
|
||||
`find "${appPath}" -name "${xcodeProjectName}" -type d -print`,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
const {execSync} = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Find the directory containing the Xcode project within the app path
|
||||
* @param {string} appPath - The root app path to search in
|
||||
* @param {string} xcodeProjectName - The name of the Xcode project file (e.g., 'HelloWorld.xcodeproj')
|
||||
* @returns {string} - The path to the directory containing the Xcode project
|
||||
*/
|
||||
function findXcodeProjectDirectory(
|
||||
appPath /*: string */,
|
||||
xcodeProjectName /*: string */,
|
||||
) /*: string */ {
|
||||
try {
|
||||
// Use find command to search for the Xcode project
|
||||
const findCommand = `find "${appPath}" -name "${xcodeProjectName}" -type d -print`;
|
||||
const result = execSync(findCommand, {encoding: 'utf8'}).trim();
|
||||
|
||||
if (!result) {
|
||||
throw new Error(
|
||||
`Xcode project '${xcodeProjectName}' not found in '${appPath}' or its subdirectories`,
|
||||
);
|
||||
}
|
||||
|
||||
// Return the directory containing the Xcode project (parent of the .xcodeproj file)
|
||||
return path.dirname(result);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to find Xcode project '${xcodeProjectName}': ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
findXcodeProjectDirectory,
|
||||
};
|
||||
Reference in New Issue
Block a user