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