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