This repository has been archived by the owner on Jan 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 477
/
BundleIdentifier.ts
185 lines (160 loc) · 5.77 KB
/
BundleIdentifier.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import plist, { PlistObject } from '@expo/plist';
import fs from 'fs-extra';
import xcode from 'xcode';
import { ExpoConfig } from '../Config.types';
import { InfoPlist } from './IosConfig.types';
import { getAllInfoPlistPaths, getAllPBXProjectPaths, getPBXProjectPath } from './Paths';
import {
ConfigurationSectionEntry,
findFirstNativeTarget,
getBuildConfigurationForId,
} from './utils/Xcodeproj';
function getBundleIdentifier(config: ExpoConfig): string | null {
return config.ios?.bundleIdentifier ?? null;
}
/**
* In Turtle v1 we set the bundleIdentifier directly on Info.plist rather
* than in pbxproj
*/
function setBundleIdentifier(config: ExpoConfig, infoPlist: InfoPlist): InfoPlist {
const bundleIdentifier = getBundleIdentifier(config);
if (!bundleIdentifier) {
return infoPlist;
}
return {
...infoPlist,
CFBundleIdentifier: bundleIdentifier,
};
}
/**
* Gets the bundle identifier of the Xcode project found in the project directory.
* If either the Xcode project doesn't exist or the project is not configured
* this function returns null.
*
* @param {string} projectRoot Path to project root containing the ios directory
* @returns {string | null} bundle identifier of the Xcode project or null if the project is not configured
*/
function getBundleIdentifierFromPbxproj(projectRoot: string): string | null {
let pbxprojPath: string;
try {
pbxprojPath = getPBXProjectPath(projectRoot);
} catch {
return null;
}
const project = xcode.project(pbxprojPath);
project.parseSync();
const nativeTarget = findFirstNativeTarget(project);
for (const [, item] of getBuildConfigurationForId(project, nativeTarget.buildConfigurationList)) {
const bundleIdentifierRaw = item.buildSettings.PRODUCT_BUNDLE_IDENTIFIER;
if (bundleIdentifierRaw) {
const bundleIdentifier =
bundleIdentifierRaw[0] === '"' ? bundleIdentifierRaw.slice(1, -1) : bundleIdentifierRaw;
// it's possible to use interpolation for the bundle identifier
// the most common case is when the last part of the id is set to `$(PRODUCT_NAME:rfc1034identifier)`
// in this case, PRODUCT_NAME should be replaced with its value
// the `rfc1034identifier` modifier replaces all non-alphanumeric characters with dashes
const bundleIdentifierParts = bundleIdentifier.split('.');
if (
bundleIdentifierParts[bundleIdentifierParts.length - 1] ===
'$(PRODUCT_NAME:rfc1034identifier)' &&
item.buildSettings.PRODUCT_NAME
) {
bundleIdentifierParts[
bundleIdentifierParts.length - 1
] = item.buildSettings.PRODUCT_NAME.replace(/[^a-zA-Z0-9]/g, '-');
}
return bundleIdentifierParts.join('.');
}
}
return null;
}
/**
* Updates the bundle identifier for a given pbxproj
*
* @param {string} pbxprojPath Path to pbxproj file
* @param {string} bundleIdentifier Bundle identifier to set in the pbxproj
* @param {boolean} [updateProductName=true] Whether to update PRODUCT_NAME
*/
function updateBundleIdentifierForPbxproj(
pbxprojPath: string,
bundleIdentifier: string,
updateProductName: boolean = true
): void {
const project = xcode.project(pbxprojPath);
project.parseSync();
const nativeTarget = findFirstNativeTarget(project);
getBuildConfigurationForId(project, nativeTarget.buildConfigurationList).forEach(
([, item]: ConfigurationSectionEntry) => {
if (item.buildSettings.PRODUCT_BUNDLE_IDENTIFIER === bundleIdentifier) {
return;
}
item.buildSettings.PRODUCT_BUNDLE_IDENTIFIER = `"${bundleIdentifier}"`;
if (updateProductName) {
const productName = bundleIdentifier.split('.').pop();
if (!productName?.includes('$')) {
item.buildSettings.PRODUCT_NAME = productName;
}
}
}
);
fs.writeFileSync(pbxprojPath, project.writeSync());
}
/**
* Updates the bundle identifier for pbx projects inside the ios directory of the given project root
*
* @param {string} projectRoot Path to project root containing the ios directory
* @param {string} bundleIdentifier Desired bundle identifier
* @param {boolean} [updateProductName=true] Whether to update PRODUCT_NAME
*/
function setBundleIdentifierForPbxproj(
projectRoot: string,
bundleIdentifier: string,
updateProductName: boolean = true
): void {
// Get all pbx projects in the ${projectRoot}/ios directory
let pbxprojPaths: string[] = [];
try {
pbxprojPaths = getAllPBXProjectPaths(projectRoot);
} catch {}
for (const pbxprojPath of pbxprojPaths) {
updateBundleIdentifierForPbxproj(pbxprojPath, bundleIdentifier, updateProductName);
}
}
/**
* Reset bundle identifier field in Info.plist to use PRODUCT_BUNDLE_IDENTIFIER, as recommended by Apple.
*/
const defaultBundleId = '$(PRODUCT_BUNDLE_IDENTIFIER)';
function resetAllPlistBundleIdentifiers(projectRoot: string): void {
const infoPlistPaths = getAllInfoPlistPaths(projectRoot);
for (const plistPath of infoPlistPaths) {
resetPlistBundleIdentifier(plistPath);
}
}
function resetPlistBundleIdentifier(plistPath: string): void {
const rawPlist = fs.readFileSync(plistPath, 'utf8');
const plistObject = plist.parse(rawPlist) as PlistObject;
if (plistObject.CFBundleIdentifier) {
if (plistObject.CFBundleIdentifier === defaultBundleId) return;
// attempt to match default Info.plist format
const format = { pretty: true, indent: `\t` };
const xml = plist.build(
{
...plistObject,
CFBundleIdentifier: defaultBundleId,
},
format
);
if (xml !== rawPlist) {
fs.writeFileSync(plistPath, xml);
}
}
}
export {
getBundleIdentifier,
setBundleIdentifier,
getBundleIdentifierFromPbxproj,
updateBundleIdentifierForPbxproj,
setBundleIdentifierForPbxproj,
resetAllPlistBundleIdentifiers,
resetPlistBundleIdentifier,
};