react-native-integrate
Version:
Automate integration of additional code into React Native projects
311 lines (310 loc) • 13.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.patchXcodeProject = patchXcodeProject;
exports.patchXcodeHasFile = patchXcodeHasFile;
exports.pbxBuildPhaseObj = pbxBuildPhaseObj;
exports.longComment = longComment;
exports.normalizeBundleId = normalizeBundleId;
exports.unquote = unquote;
const path_1 = __importDefault(require("path"));
const xcode_1 = __importDefault(require("xcode"));
const pbxFile_1 = __importDefault(require("xcode/lib/pbxFile"));
const COMMENT_KEY = /_comment$/;
function patchXcodeProject(opts) {
// fixes a bug in pbxGroupByName
const pbxGroupByNameOriginal = xcode_1.default.project.prototype.pbxGroupByName;
xcode_1.default.project.prototype.pbxGroupByName = function (name) {
const result = pbxGroupByNameOriginal.call(this, name);
if (name == 'Resources' && result == null)
return { path: null };
return result;
};
// makes files to be added on top
// eslint-disable-next-line @typescript-eslint/unbound-method
const arrayPushOriginal = Array.prototype.push;
Array.prototype.push = function (item) {
return opts.push(this, item, arrayPushOriginal);
};
return () => {
xcode_1.default.project.prototype.pbxGroupByName = pbxGroupByNameOriginal;
Array.prototype.push = arrayPushOriginal;
};
}
function patchXcodeHasFile() {
// force add file
const hasFileOriginal = xcode_1.default.project.prototype.hasFile;
xcode_1.default.project.prototype.hasFile = function () {
return false;
};
return () => {
xcode_1.default.project.prototype.hasFile = hasFileOriginal;
};
}
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return*/
// noinspection JSUnusedGlobalSymbols
xcode_1.default.project.prototype.addExtensionTarget = function (name, type, subfolder, opts) {
// Setup uuid and name of new target
const targetUuid = this.generateUuid(), targetType = type, targetSubfolder = subfolder || name, targetName = name.trim(), targetBundleId = opts.bundleId, targetTeam = opts.team, targetCodeSign = opts.codeSign;
// Build Configuration: Create
let buildConfigurationsList = [
{
name: 'Debug',
isa: 'XCBuildConfiguration',
buildSettings: {
CLANG_ANALYZER_NONNULL: 'YES',
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION: 'YES_AGGRESSIVE',
CLANG_CXX_LANGUAGE_STANDARD: '"gnu++20"',
CLANG_ENABLE_OBJC_WEAK: 'YES',
CLANG_WARN_DOCUMENTATION_COMMENTS: 'YES',
CLANG_WARN_UNGUARDED_AVAILABILITY: 'YES_AGGRESSIVE',
CODE_SIGN_STYLE: 'Automatic',
CURRENT_PROJECT_VERSION: '1',
DEBUG_INFORMATION_FORMAT: 'dwarf',
GCC_C_LANGUAGE_STANDARD: 'gnu11',
GENERATE_INFOPLIST_FILE: 'YES',
INFOPLIST_FILE: `"${path_1.default.join(targetSubfolder, 'Info.plist')}"`,
INFOPLIST_KEY_CFBundleDisplayName: `"${targetSubfolder}"`,
INFOPLIST_KEY_NSHumanReadableCopyright: '""',
IPHONEOS_DEPLOYMENT_TARGET: '16.4',
LD_RUNPATH_SEARCH_PATHS: [
'"$(inherited)"',
'"@executable_path/Frameworks"',
'"@executable_path/../../Frameworks"',
],
MARKETING_VERSION: '1.0',
MTL_ENABLE_DEBUG_INFO: 'INCLUDE_SOURCE',
MTL_FAST_MATH: 'YES',
PRODUCT_NAME: '"$(TARGET_NAME)"',
SKIP_INSTALL: 'YES',
SWIFT_EMIT_LOC_STRINGS: 'YES',
TARGETED_DEVICE_FAMILY: '"1,2"',
},
},
{
name: 'Release',
isa: 'XCBuildConfiguration',
buildSettings: {
CLANG_ANALYZER_NONNULL: 'YES',
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION: 'YES_AGGRESSIVE',
CLANG_CXX_LANGUAGE_STANDARD: '"gnu++20"',
CLANG_ENABLE_OBJC_WEAK: 'YES',
CLANG_WARN_DOCUMENTATION_COMMENTS: 'YES',
CLANG_WARN_UNGUARDED_AVAILABILITY: 'YES_AGGRESSIVE',
CODE_SIGN_STYLE: 'Automatic',
COPY_PHASE_STRIP: 'NO',
CURRENT_PROJECT_VERSION: '1',
DEBUG_INFORMATION_FORMAT: '"dwarf-with-dsym"',
GCC_C_LANGUAGE_STANDARD: 'gnu11',
GENERATE_INFOPLIST_FILE: 'YES',
INFOPLIST_FILE: `"${path_1.default.join(targetSubfolder, 'Info.plist')}"`,
INFOPLIST_KEY_CFBundleDisplayName: `"${targetSubfolder}"`,
INFOPLIST_KEY_NSHumanReadableCopyright: '""',
IPHONEOS_DEPLOYMENT_TARGET: '16.4',
LD_RUNPATH_SEARCH_PATHS: [
'"$(inherited)"',
'"@executable_path/Frameworks"',
'"@executable_path/../../Frameworks"',
],
MARKETING_VERSION: '1.0',
MTL_FAST_MATH: 'YES',
PRODUCT_NAME: '"$(TARGET_NAME)"',
SKIP_INSTALL: 'YES',
SWIFT_EMIT_LOC_STRINGS: 'YES',
TARGETED_DEVICE_FAMILY: '"1,2"',
},
},
];
// Add optional bundleId to build configuration
buildConfigurationsList = buildConfigurationsList.map(elem => {
if (targetBundleId) {
elem.buildSettings.PRODUCT_BUNDLE_IDENTIFIER = '"' + targetBundleId + '"';
}
if (targetTeam) {
elem.buildSettings.DEVELOPMENT_TEAM = '"' + targetTeam + '"';
}
if (targetCodeSign) {
elem.buildSettings.CODE_SIGN_IDENTITY = '"' + targetCodeSign + '"';
}
return elem;
});
// Build Configuration: Add
const buildConfigurations = this.addXCConfigurationList(buildConfigurationsList, 'Release', 'Build configuration list for PBXNativeTarget "' + targetName + '"');
// Product: Create
const productName = targetName, productType = 'com.apple.product-type.app-extension', productFileType = '"wrapper.app-extension"', productFile = this.addProductFile(productName, {
group: 'Embed Foundation Extensions',
target: targetUuid,
explicitFileType: productFileType,
});
productFile.settings = {
ATTRIBUTES: ['RemoveHeadersOnCopy'],
};
// Product: Add to build file list
this.addToPbxBuildFileSection(productFile);
// Target: Create
const target = {
uuid: targetUuid,
pbxNativeTarget: {
isa: 'PBXNativeTarget',
name: '"' + targetName + '"',
productName: '"' + targetName + '"',
productReference: productFile.fileRef,
productType: `"${productType}"`,
buildConfigurationList: buildConfigurations.uuid,
buildPhases: [],
buildRules: [],
dependencies: [],
},
};
// Target: Add to PBXNativeTarget section
this.addToPbxNativeTargetSection(target);
// Create CopyFiles phase in first target
let sources = this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Foundation Extensions', productFile.target);
if (!sources) {
this.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Embed Foundation Extensions', this.getFirstTarget().uuid, targetType);
// Add product to Embed Foundation Extensions phase
sources = this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Foundation Extensions', productFile.target);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
sources.files.push(pbxBuildPhaseObj(productFile));
// Target: Add uuid to root project
this.addToPbxProjectSection(target);
// Target: Add dependency for this target to other targets
this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]);
// Return target on success
return target;
};
// noinspection JSUnusedGlobalSymbols
xcode_1.default.project.prototype.addExtensionSourceFile = function (path, opt, group) {
const file = this.addFile(path, group, opt);
file.target = opt.target;
file.uuid = this.generateUuid();
this.addToPbxBuildFileSection(file); // PBXBuildFile
// this.addToPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase
return file;
};
// noinspection JSUnusedGlobalSymbols
xcode_1.default.project.prototype.addExtensionResourceFile = function (path, opt, group) {
// noinspection JSPotentiallyInvalidConstructorUsage
const file = new pbxFile_1.default(path, opt);
file.uuid = this.generateUuid();
file.target = opt.target;
file.fileRef = this.generateUuid();
this.addToPbxFileReferenceSection(file); // PBXFileReference
if (this.getPBXVariantGroupByKey(group)) {
this.addToPbxVariantGroup(file, group); // PBXVariantGroup
}
return file;
};
// noinspection JSUnusedGlobalSymbols
xcode_1.default.project.prototype.addExtensionLocalizationVariantGroup = function (name, target) {
const groupKey = this.pbxCreateVariantGroup(name);
const resourceGroupKey = this.findPBXGroupKey({ name: 'Resources' });
this.addToPbxGroup(groupKey, resourceGroupKey);
const localizationVariantGroup = {
uuid: this.generateUuid(),
fileRef: groupKey,
basename: name,
group: 'Resources',
target,
};
this.addToPbxBuildFileSection(localizationVariantGroup); // PBXBuildFile
this.addToPbxResourcesBuildPhase(localizationVariantGroup); //PBXResourcesBuildPhase
return localizationVariantGroup;
};
xcode_1.default.project.prototype.findPBXGroupKeyByAny = function (nameOrPath) {
const groups = this.hash.project.objects['PBXGroup'];
let target;
for (const key in groups) {
// only look for comments
if (COMMENT_KEY.test(key))
continue;
const group = groups[key];
if (nameOrPath) {
if (nameOrPath === group.path || nameOrPath === group.name) {
target = key;
break;
}
}
}
return target;
};
xcode_1.default.project.prototype.getBuildPropertyByTarget = function (prop, build, target) {
let value;
const validConfigs = [];
if (target) {
const targetBuildConfigs = target.buildConfigurationList;
const xcConfigList = this.pbxXCConfigurationList();
// Collect the UUID's from the configuration of our target
for (const configName in xcConfigList) {
if (!COMMENT_KEY.test(configName) && targetBuildConfigs === configName) {
const buildVariants = xcConfigList[configName].buildConfigurations;
for (const item of buildVariants) {
validConfigs.push(item.value);
}
break;
}
}
}
const configs = this.pbxXCBuildConfigurationSection();
for (const configName in configs) {
if (!COMMENT_KEY.test(configName)) {
if (target && !validConfigs.includes(configName))
continue;
const config = configs[configName];
if ((build && config.name === build) || build === undefined) {
if (config.buildSettings[prop] !== undefined) {
value = config.buildSettings[prop];
}
}
}
}
return value;
};
xcode_1.default.project.prototype.updateBuildPropertyByTarget = function (prop, value, build, target) {
const validConfigs = [];
if (target) {
const targetBuildConfigs = target.buildConfigurationList;
const xcConfigList = this.pbxXCConfigurationList();
// Collect the UUID's from the configuration of our target
for (const configName in xcConfigList) {
if (!COMMENT_KEY.test(configName) && targetBuildConfigs === configName) {
const buildVariants = xcConfigList[configName].buildConfigurations;
for (const item of buildVariants) {
validConfigs.push(item.value);
}
break;
}
}
}
const configs = this.pbxXCBuildConfigurationSection();
for (const configName in configs) {
if (!COMMENT_KEY.test(configName)) {
if (target && !validConfigs.includes(configName))
continue;
const config = configs[configName];
if ((build && config.name === build) || !build) {
config.buildSettings[prop] = value;
}
}
}
};
function pbxBuildPhaseObj(file) {
const obj = Object.create(null);
obj.value = file.uuid;
obj.comment = longComment(file);
return obj;
}
function longComment(file) {
return `${file.basename} in ${file.group}`;
}
function normalizeBundleId(bundleId, opts) {
return bundleId
.replace(/"/g, '')
.replace(/\$\(PRODUCT_NAME.*?\)/g, opts.productName);
}
function unquote(str) {
return str?.replace(/^"(.*)"$/, '$1');
}