Skip to content

Commit ff11dd4

Browse files
committed
refactor: cleanup
1 parent 8bd2cc5 commit ff11dd4

4 files changed

Lines changed: 214 additions & 181 deletions

File tree

packages/react-native-brownfield/src/expo-config-plugin/ios/__tests__/xcodeHelpers.test.ts renamed to packages/react-native-brownfield/src/expo-config-plugin/ios/__tests__/expo-updates.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
ensureFrameworkHasExpoPlistResource,
33
selectExpoPlistFileReference,
4-
} from '../xcodeHelpers';
4+
} from '../utils/expo-updates';
55

66
describe('selectExpoPlistFileReference', () => {
77
it('selects the exact Supporting/Expo.plist reference when present', () => {
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import type { XcodeProject } from '@expo/config-plugins';
2+
3+
import { Logger } from '../../logging';
4+
import { SourceModificationError } from '../../errors/SourceModificationError';
5+
import { ensureTargetHasFileReferenceInResourcesBuildPhase } from '../xcodeHelpers';
6+
7+
type PbxFileReference = {
8+
[key: string]: unknown;
9+
name?: string;
10+
path?: string;
11+
};
12+
13+
type PbxGroupLike = {
14+
[key: string]: unknown;
15+
name?: string;
16+
path?: string;
17+
children?: Array<{ value?: string; comment?: string } | string>;
18+
};
19+
20+
const EXPO_PLIST_FILE_NAME = 'Expo.plist';
21+
const EXPO_PLIST_PRIMARY_RELATIVE_PATH = 'Supporting/Expo.plist';
22+
const RESOURCES_BUILD_PHASE_COMMENT = 'Resources test comment';
23+
const EXPO_PLIST_RESOURCE_COMMENT = 'Expo.plist in Resources';
24+
25+
function normalizePbxString(value: unknown): string {
26+
return String(value ?? '').replace(/^"(.*)"$/, '$1');
27+
}
28+
29+
function normalizePbxPathLike(value: unknown): string {
30+
return normalizePbxString(value).replace(/^\.\//, '');
31+
}
32+
33+
function isSupportingGroup(group: PbxGroupLike): boolean {
34+
const groupName = normalizePbxString(group.name);
35+
const groupPath = normalizePbxString(group.path);
36+
37+
return (
38+
groupName === 'Supporting' ||
39+
groupPath === 'Supporting' ||
40+
groupPath.endsWith('/Supporting')
41+
);
42+
}
43+
44+
function groupContainsFileReference(
45+
group: PbxGroupLike,
46+
fileRefUuid: string
47+
): boolean {
48+
const children = Array.isArray(group.children) ? group.children : [];
49+
50+
return children.some((child) => {
51+
if (typeof child === 'string') {
52+
return child === fileRefUuid;
53+
}
54+
55+
return child?.value === fileRefUuid;
56+
});
57+
}
58+
59+
function isPrimaryExpoPlistMatch(
60+
fileRefUuid: string,
61+
fileRef: PbxFileReference,
62+
groups?: Record<string, PbxGroupLike | string>
63+
): boolean {
64+
const normalizedPath = normalizePbxPathLike(fileRef.path);
65+
const fileName = normalizePbxString(fileRef.name);
66+
67+
if (
68+
normalizedPath === EXPO_PLIST_PRIMARY_RELATIVE_PATH ||
69+
normalizedPath.endsWith(`/${EXPO_PLIST_PRIMARY_RELATIVE_PATH}`)
70+
) {
71+
return true;
72+
}
73+
74+
if (
75+
normalizedPath !== EXPO_PLIST_FILE_NAME &&
76+
fileName !== EXPO_PLIST_FILE_NAME
77+
) {
78+
return false;
79+
}
80+
81+
return Object.entries(groups ?? {}).some(([groupUuid, group]) => {
82+
if (groupUuid.endsWith('_comment') || typeof group === 'string') {
83+
return false;
84+
}
85+
86+
return (
87+
isSupportingGroup(group) && groupContainsFileReference(group, fileRefUuid)
88+
);
89+
});
90+
}
91+
92+
/**
93+
* Selects an existing PBXFileReference for the app-level Expo plist.
94+
*
95+
* Matches either:
96+
* - a file reference whose own path is `Supporting/Expo.plist`
97+
* - or an `Expo.plist` file reference that lives under a `Supporting` PBXGroup
98+
*/
99+
export function selectExpoPlistFileReference(
100+
fileReferences: Record<string, PbxFileReference | string>,
101+
groups?: Record<string, PbxGroupLike | string>
102+
): string | null {
103+
for (const [fileRefUuid, fileRef] of Object.entries(fileReferences)) {
104+
if (fileRefUuid.endsWith('_comment') || typeof fileRef === 'string') {
105+
continue;
106+
}
107+
108+
if (isPrimaryExpoPlistMatch(fileRefUuid, fileRef, groups)) {
109+
return fileRefUuid;
110+
}
111+
}
112+
113+
return null;
114+
}
115+
116+
function getExpoPlistFileRefOrThrow(project: XcodeProject): string {
117+
const fileReferences = project.pbxFileReferenceSection() as Record<
118+
string,
119+
PbxFileReference | string
120+
>;
121+
const groups = (project as any).hash?.project?.objects?.PBXGroup as
122+
| Record<string, PbxGroupLike | string>
123+
| undefined;
124+
const existingExpoPlistFileRefUuid = selectExpoPlistFileReference(
125+
fileReferences,
126+
groups
127+
);
128+
129+
if (existingExpoPlistFileRefUuid) {
130+
return existingExpoPlistFileRefUuid;
131+
}
132+
133+
throw new SourceModificationError(
134+
`Could not find the "${EXPO_PLIST_PRIMARY_RELATIVE_PATH}" PBXFileReference needed for Expo.plist resource wiring`
135+
);
136+
}
137+
138+
/**
139+
* Ensures the framework target contains the app-level `Supporting/Expo.plist`
140+
* in a `PBXResourcesBuildPhase`. This is idempotent and safe to call repeatedly.
141+
*/
142+
export function ensureFrameworkHasExpoPlistResource(
143+
project: XcodeProject,
144+
frameworkTargetUUID: string
145+
): void {
146+
const expoPlistFileRefUuid = getExpoPlistFileRefOrThrow(project);
147+
const didAddExpoPlistResource =
148+
ensureTargetHasFileReferenceInResourcesBuildPhase(
149+
project,
150+
frameworkTargetUUID,
151+
expoPlistFileRefUuid,
152+
{
153+
resourcesBuildPhaseComment: RESOURCES_BUILD_PHASE_COMMENT,
154+
buildFileComment: EXPO_PLIST_RESOURCE_COMMENT,
155+
}
156+
);
157+
158+
if (!didAddExpoPlistResource) {
159+
Logger.logDebug(
160+
'Framework resources already include Supporting/Expo.plist'
161+
);
162+
return;
163+
}
164+
165+
Logger.logDebug(
166+
'Added Supporting/Expo.plist to framework PBXResourcesBuildPhase'
167+
);
168+
}

packages/react-native-brownfield/src/expo-config-plugin/ios/withBrownfieldIos.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import {
99
addFrameworkTarget,
1010
addSourceFilesBuildPhase,
1111
copyBundleReactNativePhase,
12-
ensureFrameworkHasExpoPlistResource,
1312
} from './xcodeHelpers';
1413
import { modifyPodfile } from './podfileHelpers';
14+
import { ensureFrameworkHasExpoPlistResource } from './utils/expo-updates';
1515
import { withIosFrameworkFiles } from './withIosFrameworkFiles';
1616
import type { ResolvedBrownfieldPluginConfigWithIos } from '../types';
1717
import { Logger } from '../logging';

0 commit comments

Comments
 (0)