1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4  value: true
5});
6exports.getBundleIdentifier = getBundleIdentifier;
7exports.getBundleIdentifierFromPbxproj = getBundleIdentifierFromPbxproj;
8exports.resetAllPlistBundleIdentifiers = resetAllPlistBundleIdentifiers;
9exports.resetPlistBundleIdentifier = resetPlistBundleIdentifier;
10exports.setBundleIdentifier = setBundleIdentifier;
11exports.setBundleIdentifierForPbxproj = setBundleIdentifierForPbxproj;
12exports.updateBundleIdentifierForPbxproj = updateBundleIdentifierForPbxproj;
13exports.withBundleIdentifier = void 0;
14
15function _plist() {
16  const data = _interopRequireDefault(require("@expo/plist"));
17
18  _plist = function () {
19    return data;
20  };
21
22  return data;
23}
24
25function _assert() {
26  const data = _interopRequireDefault(require("assert"));
27
28  _assert = function () {
29    return data;
30  };
31
32  return data;
33}
34
35function _fs() {
36  const data = _interopRequireDefault(require("fs"));
37
38  _fs = function () {
39    return data;
40  };
41
42  return data;
43}
44
45function _xcode() {
46  const data = _interopRequireDefault(require("xcode"));
47
48  _xcode = function () {
49    return data;
50  };
51
52  return data;
53}
54
55function _withDangerousMod() {
56  const data = require("../plugins/withDangerousMod");
57
58  _withDangerousMod = function () {
59    return data;
60  };
61
62  return data;
63}
64
65function _Paths() {
66  const data = require("./Paths");
67
68  _Paths = function () {
69    return data;
70  };
71
72  return data;
73}
74
75function _Target() {
76  const data = require("./Target");
77
78  _Target = function () {
79    return data;
80  };
81
82  return data;
83}
84
85function _Xcodeproj() {
86  const data = require("./utils/Xcodeproj");
87
88  _Xcodeproj = function () {
89    return data;
90  };
91
92  return data;
93}
94
95function _string() {
96  const data = require("./utils/string");
97
98  _string = function () {
99    return data;
100  };
101
102  return data;
103}
104
105function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
106
107const withBundleIdentifier = (config, {
108  bundleIdentifier
109}) => {
110  return (0, _withDangerousMod().withDangerousMod)(config, ['ios', async config => {
111    var _config$ios;
112
113    const bundleId = bundleIdentifier !== null && bundleIdentifier !== void 0 ? bundleIdentifier : (_config$ios = config.ios) === null || _config$ios === void 0 ? void 0 : _config$ios.bundleIdentifier;
114    (0, _assert().default)(bundleId, '`bundleIdentifier` must be defined in the app config (`expo.ios.bundleIdentifier`) or passed to the plugin `withBundleIdentifier`.');
115    await setBundleIdentifierForPbxproj(config.modRequest.projectRoot, bundleId);
116    return config;
117  }]);
118};
119
120exports.withBundleIdentifier = withBundleIdentifier;
121
122function getBundleIdentifier(config) {
123  var _config$ios$bundleIde, _config$ios2;
124
125  return (_config$ios$bundleIde = (_config$ios2 = config.ios) === null || _config$ios2 === void 0 ? void 0 : _config$ios2.bundleIdentifier) !== null && _config$ios$bundleIde !== void 0 ? _config$ios$bundleIde : null;
126}
127/**
128 * In Turtle v1 we set the bundleIdentifier directly on Info.plist rather
129 * than in pbxproj
130 */
131
132
133function setBundleIdentifier(config, infoPlist) {
134  const bundleIdentifier = getBundleIdentifier(config);
135
136  if (!bundleIdentifier) {
137    return infoPlist;
138  }
139
140  return { ...infoPlist,
141    CFBundleIdentifier: bundleIdentifier
142  };
143}
144/**
145 * Gets the bundle identifier defined in the Xcode project found in the project directory.
146 *
147 * A bundle identifier is stored as a value in XCBuildConfiguration entry.
148 * Those entries exist for every pair (build target, build configuration).
149 * Unless target name is passed, the first target defined in the pbxproj is used
150 * (to keep compatibility with the inaccurate legacy implementation of this function).
151 * The build configuration is usually 'Release' or 'Debug'. However, it could be any arbitrary string.
152 * Defaults to 'Release'.
153 *
154 * @param {string} projectRoot Path to project root containing the ios directory
155 * @param {string} targetName Target name
156 * @param {string} buildConfiguration Build configuration. Defaults to 'Release'.
157 * @returns {string | null} bundle identifier of the Xcode project or null if the project is not configured
158 */
159
160
161function getBundleIdentifierFromPbxproj(projectRoot, {
162  targetName,
163  buildConfiguration = 'Release'
164} = {}) {
165  let pbxprojPath;
166
167  try {
168    pbxprojPath = (0, _Paths().getPBXProjectPath)(projectRoot);
169  } catch {
170    return null;
171  }
172
173  const project = _xcode().default.project(pbxprojPath);
174
175  project.parseSync();
176  const xcBuildConfiguration = (0, _Target().getXCBuildConfigurationFromPbxproj)(project, {
177    targetName,
178    buildConfiguration
179  });
180
181  if (!xcBuildConfiguration) {
182    return null;
183  }
184
185  return getProductBundleIdentifierFromBuildConfiguration(xcBuildConfiguration);
186}
187
188function getProductBundleIdentifierFromBuildConfiguration(xcBuildConfiguration) {
189  const bundleIdentifierRaw = xcBuildConfiguration.buildSettings.PRODUCT_BUNDLE_IDENTIFIER;
190
191  if (bundleIdentifierRaw) {
192    const bundleIdentifier = (0, _string().trimQuotes)(bundleIdentifierRaw); // it's possible to use interpolation for the bundle identifier
193    // the most common case is when the last part of the id is set to `$(PRODUCT_NAME:rfc1034identifier)`
194    // in this case, PRODUCT_NAME should be replaced with its value
195    // the `rfc1034identifier` modifier replaces all non-alphanumeric characters with dashes
196
197    const bundleIdentifierParts = bundleIdentifier.split('.');
198
199    if (bundleIdentifierParts[bundleIdentifierParts.length - 1] === '$(PRODUCT_NAME:rfc1034identifier)' && xcBuildConfiguration.buildSettings.PRODUCT_NAME) {
200      bundleIdentifierParts[bundleIdentifierParts.length - 1] = xcBuildConfiguration.buildSettings.PRODUCT_NAME.replace(/[^a-zA-Z0-9]/g, '-');
201    }
202
203    return bundleIdentifierParts.join('.');
204  } else {
205    return null;
206  }
207}
208/**
209 * Updates the bundle identifier for a given pbxproj
210 *
211 * @param {string} pbxprojPath Path to pbxproj file
212 * @param {string} bundleIdentifier Bundle identifier to set in the pbxproj
213 * @param {boolean} [updateProductName=true]  Whether to update PRODUCT_NAME
214 */
215
216
217function updateBundleIdentifierForPbxproj(pbxprojPath, bundleIdentifier, updateProductName = true) {
218  const project = _xcode().default.project(pbxprojPath);
219
220  project.parseSync();
221  const [, nativeTarget] = (0, _Target().findFirstNativeTarget)(project);
222  (0, _Xcodeproj().getBuildConfigurationsForListId)(project, nativeTarget.buildConfigurationList).forEach(([, item]) => {
223    if (item.buildSettings.PRODUCT_BUNDLE_IDENTIFIER === bundleIdentifier) {
224      return;
225    }
226
227    item.buildSettings.PRODUCT_BUNDLE_IDENTIFIER = `"${bundleIdentifier}"`;
228
229    if (updateProductName) {
230      const productName = bundleIdentifier.split('.').pop();
231
232      if (!(productName !== null && productName !== void 0 && productName.includes('$'))) {
233        item.buildSettings.PRODUCT_NAME = productName;
234      }
235    }
236  });
237
238  _fs().default.writeFileSync(pbxprojPath, project.writeSync());
239}
240/**
241 * Updates the bundle identifier for pbx projects inside the ios directory of the given project root
242 *
243 * @param {string} projectRoot Path to project root containing the ios directory
244 * @param {string} bundleIdentifier Desired bundle identifier
245 * @param {boolean} [updateProductName=true]  Whether to update PRODUCT_NAME
246 */
247
248
249function setBundleIdentifierForPbxproj(projectRoot, bundleIdentifier, updateProductName = true) {
250  // Get all pbx projects in the ${projectRoot}/ios directory
251  let pbxprojPaths = [];
252
253  try {
254    pbxprojPaths = (0, _Paths().getAllPBXProjectPaths)(projectRoot);
255  } catch {}
256
257  for (const pbxprojPath of pbxprojPaths) {
258    updateBundleIdentifierForPbxproj(pbxprojPath, bundleIdentifier, updateProductName);
259  }
260}
261/**
262 * Reset bundle identifier field in Info.plist to use PRODUCT_BUNDLE_IDENTIFIER, as recommended by Apple.
263 */
264
265
266const defaultBundleId = '$(PRODUCT_BUNDLE_IDENTIFIER)';
267
268function resetAllPlistBundleIdentifiers(projectRoot) {
269  const infoPlistPaths = (0, _Paths().getAllInfoPlistPaths)(projectRoot);
270
271  for (const plistPath of infoPlistPaths) {
272    resetPlistBundleIdentifier(plistPath);
273  }
274}
275
276function resetPlistBundleIdentifier(plistPath) {
277  const rawPlist = _fs().default.readFileSync(plistPath, 'utf8');
278
279  const plistObject = _plist().default.parse(rawPlist);
280
281  if (plistObject.CFBundleIdentifier) {
282    if (plistObject.CFBundleIdentifier === defaultBundleId) return; // attempt to match default Info.plist format
283
284    const format = {
285      pretty: true,
286      indent: `\t`
287    };
288
289    const xml = _plist().default.build({ ...plistObject,
290      CFBundleIdentifier: defaultBundleId
291    }, format);
292
293    if (xml !== rawPlist) {
294      _fs().default.writeFileSync(plistPath, xml);
295    }
296  }
297}
298//# sourceMappingURL=BundleIdentifier.js.map