react-native-code-push
Version:
React Native plugin for the CodePush service
131 lines (112 loc) • 6.12 kB
JavaScript
var glob = require("glob");
var path = require("path");
var xcode = require("xcode");
var packageFile = require('../../../../package.json');
var ignoreNodeModules = { ignore: "node_modules/**" };
var ignoreNodeModulesAndPods = { ignore: ["node_modules/**", "ios/Pods/**"] };
var appDelegatePaths = glob.sync("**/AppDelegate.+(mm|m)", ignoreNodeModules);
exports.codePushHeaderImportStatement = `#import <CodePush/CodePush.h>`;
exports.codePushHeaderImportStatementFormatted = `\n${this.codePushHeaderImportStatement}`;
exports.codePushBundleUrl = "[CodePush bundleURL]";
exports.oldBundleUrl = "[[NSBundle mainBundle] URLForResource:@\"main\" withExtension:@\"jsbundle\"]";
exports.linkedJsCodeLocationAssignmentStatement = "jsCodeLocation = [CodePush bundleURL];";
exports.getJsCodeLocationPatch = function(defaultJsCodeLocationAssignmentStatement) {
return `
#ifdef DEBUG
${defaultJsCodeLocationAssignmentStatement}
#else
${this.linkedJsCodeLocationAssignmentStatement}
#endif`;
}
// Fix for https://github.com/microsoft/react-native-code-push/issues/477
// Typical location of AppDelegate.m for newer RN versions: $PROJECT_ROOT/ios/<project_name>/AppDelegate.m
// Let's try to find that path by filtering the whole array for any path containing <project_name>
// If we can't find it there, play dumb and pray it is the first path we find.
exports.getAppDeletePath = function() {
return findFileByAppName(appDelegatePaths, packageFile ? packageFile.name : null) || appDelegatePaths[0];
}
exports.getPlistPath = function() {
var xcodeProjectPaths = glob.sync(`**/*.xcodeproj/project.pbxproj`, ignoreNodeModulesAndPods);
if (!xcodeProjectPaths){
return getDefaultPlistPath();
}
if (xcodeProjectPaths.length !== 1) {
console.log('Could not determine correct xcode proj path to retrieve related plist file, there are multiple xcodeproj under the solution.');
return getDefaultPlistPath();
}
var xcodeProjectPath = xcodeProjectPaths[0];
var parsedXCodeProj;
try {
var proj = xcode.project(xcodeProjectPath);
//use sync version because there are some problems with async version of xcode lib as of current version
parsedXCodeProj = proj.parseSync();
}
catch(e) {
console.log('Couldn\'t read info.plist path from xcode project - error: ' + e.message);
return getDefaultPlistPath();
}
var INFO_PLIST_PROJECT_KEY = 'INFOPLIST_FILE';
var RELEASE_BUILD_PROPERTY_NAME = "Release";
var targetProductName = packageFile ? packageFile.name : null;
//Try to get 'Release' build of ProductName matching the package name first and if it doesn't exist then try to get any other if existing
var plistPathValue = getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY, targetProductName, RELEASE_BUILD_PROPERTY_NAME) ||
getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY, targetProductName) ||
getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY, null, RELEASE_BUILD_PROPERTY_NAME) ||
getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY) ||
parsedXCodeProj.getBuildProperty(INFO_PLIST_PROJECT_KEY, RELEASE_BUILD_PROPERTY_NAME) ||
parsedXCodeProj.getBuildProperty(INFO_PLIST_PROJECT_KEY);
if (!plistPathValue){
return getDefaultPlistPath();
}
//also remove surrounding quotes from plistPathValue to get correct path resolved
//(see https://github.com/microsoft/react-native-code-push/issues/534#issuecomment-302069326 for details)
return path.resolve(path.dirname(xcodeProjectPath), '..', plistPathValue.replace(/^"(.*)"$/, '$1'));
}
// Helper that filters an array with AppDelegate.m paths for a path with the app name inside it
// Should cover nearly all cases
function findFileByAppName(array, appName) {
if (array.length === 0 || !appName) return null;
for (var i = 0; i < array.length; i++) {
var path = array[i];
if (path && path.indexOf(appName) !== -1) {
return path;
}
}
return null;
}
function getDefaultPlistPath() {
//this is old logic in case we are unable to find PLIST from xcode/pbxproj - at least we can fallback to default solution
return glob.sync(`**/${packageFile.name}/*Info.plist`, ignoreNodeModules)[0];
}
// This is enhanced version of standard implementation of xcode 'getBuildProperty' function
// but allows us to narrow results by PRODUCT_NAME property also.
// So we suppose that proj name should be the same as package name, otherwise fallback to default plist path searching logic
function getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, prop, targetProductName, build) {
var target;
var COMMENT_KEY = /_comment$/;
var PRODUCT_NAME_PROJECT_KEY = 'PRODUCT_NAME';
var TV_OS_DEPLOYMENT_TARGET_PROPERTY_NAME = 'TVOS_DEPLOYMENT_TARGET';
var TEST_HOST_PROPERTY_NAME = 'TEST_HOST';
var configs = parsedXCodeProj.pbxXCBuildConfigurationSection();
for (var configName in configs) {
if (!COMMENT_KEY.test(configName)) {
var config = configs[configName];
if ( (build && config.name === build) || (build === undefined) ) {
if (targetProductName) {
if (config.buildSettings[prop] !== undefined && config.buildSettings[PRODUCT_NAME_PROJECT_KEY] == targetProductName) {
target = config.buildSettings[prop];
}
} else {
if (config.buildSettings[prop] !== undefined &&
//exclude tvOS projects
config.buildSettings[TV_OS_DEPLOYMENT_TARGET_PROPERTY_NAME] == undefined &&
//exclude test app
config.buildSettings[TEST_HOST_PROPERTY_NAME] == undefined) {
target = config.buildSettings[prop];
}
}
}
}
}
return target;
}