@embrace-io/react-native
Version:
A React Native wrapper for the Embrace SDK
291 lines (290 loc) • 12.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findNameWithCaseSensitiveFromPath = exports.projectNameToBridgingHeader = exports.XcodeProject = exports.getXcodeProject = exports.xcodePatchable = exports.embracePlistPatchable = exports.podfilePatchable = exports.getPodFile = exports.getAppDelegateByIOSLanguage = exports.embRunScript = exports.EMBRACE_INIT_OBJECTIVEC = exports.EMBRACE_IMPORT_OBJECTIVEC = exports.exportSourcemapRNVariable = exports.makeSourcemapDirectory = exports.bundlePhaseRE = exports.embraceNativePod = void 0;
const common_1 = require("../setup/patches/common");
const EmbraceLogger_1 = require("../../src/utils/EmbraceLogger");
const file_1 = require("./file");
const osPath = require("path");
const fs = require("fs");
const xcode = require("xcode");
const glob = require("glob");
const embLogger = new EmbraceLogger_1.default(console);
exports.embraceNativePod = `pod 'EmbraceIO'`;
exports.bundlePhaseRE = /react-native-xcode\.sh/;
exports.makeSourcemapDirectory = 'mkdir -p "$CONFIGURATION_BUILD_DIR/embrace-assets"';
exports.exportSourcemapRNVariable = 'export SOURCEMAP_FILE="$CONFIGURATION_BUILD_DIR/embrace-assets/main.jsbundle.map";';
const EMBRACE_IMPORT_OBJECTIVEC = ({ bridgingHeader, }) => [`#import "${bridgingHeader}"`];
exports.EMBRACE_IMPORT_OBJECTIVEC = EMBRACE_IMPORT_OBJECTIVEC;
exports.EMBRACE_INIT_OBJECTIVEC = "[EmbraceInitializer start];";
exports.embRunScript = '"${PODS_ROOT}/EmbraceIO/run.sh"';
const getAppDelegateByIOSLanguage = (projectName, language) => {
const appDelegatePathFounded = glob.sync(`**/${common_1.MAIN_CLASS_BY_LANGUAGE[language]}`, {
ignore: ["node_modules/**", "ios/Pods/**"],
});
let appDelegatePath;
if (appDelegatePathFounded.length === 1) {
appDelegatePath = appDelegatePathFounded[0];
}
else if (appDelegatePathFounded.length > 1) {
appDelegatePath = appDelegatePathFounded.find((path) => {
return (path.toLocaleLowerCase().indexOf(projectName.toLocaleLowerCase()) > -1);
});
}
if (!appDelegatePath) {
return undefined;
}
return (0, file_1.getFileContents)(appDelegatePath);
};
exports.getAppDelegateByIOSLanguage = getAppDelegateByIOSLanguage;
const getPodFile = () => {
const podfilePath = glob.sync("ios/Podfile")[0];
if (!podfilePath) {
throw new Error("Could not find Podfile. Please refer to the docs at https://embrace.io/docs to update manually.");
}
return (0, file_1.getFileContents)(podfilePath);
};
exports.getPodFile = getPodFile;
const podfilePatchable = () => {
return new Promise((resolve, reject) => {
try {
return resolve((0, exports.getPodFile)());
}
catch (e) {
if (e instanceof Error) {
return reject(e.message);
}
return reject(e);
}
});
};
exports.podfilePatchable = podfilePatchable;
const embracePlistPatchable = () => {
return new Promise((resolve, reject) => {
const plistPath = glob.sync("ios/**/Embrace-Info.plist")[0];
if (!plistPath) {
return reject(embLogger.format("Could not find Embrace-Info.plist"));
}
return resolve((0, file_1.getFileContents)(plistPath));
});
};
exports.embracePlistPatchable = embracePlistPatchable;
const xcodePatchable = ({ name, }) => {
return new Promise((resolve, reject) => {
const projectPathFounded = glob.sync("**/*.xcodeproj/project.pbxproj", { ignore: ["node_modules/**", "ios/Pods/**"] });
let projectPath;
if (projectPathFounded.length === 1) {
projectPath = projectPathFounded[0];
}
else if (projectPathFounded.length > 1) {
projectPath = projectPathFounded.find((path) => {
return path.toLocaleLowerCase().indexOf(name.toLocaleLowerCase()) > -1;
});
}
if (!projectPath) {
return reject(embLogger.format(`Could not find xcode project file. ${docsMessage}`));
}
return (0, exports.getXcodeProject)(projectPath).then(resolve).catch(reject);
});
};
exports.xcodePatchable = xcodePatchable;
const docsMessage = "Please refer to the docs at https://embrace.io/docs to update manually.";
const getXcodeProject = (path) => {
const project = xcode.project(path);
return new Promise((resolve, reject) => {
project.parse((err) => {
if (err) {
return reject(err);
}
const proj = new XcodeProject(path, project);
resolve(proj);
});
});
};
exports.getXcodeProject = getXcodeProject;
class XcodeProject {
constructor(path = "", project) {
this.path = path;
this.project = project;
}
buildPhaseObj() {
return this.project.hash.project.objects.PBXShellScriptBuildPhase || {};
}
hasLine(key, line) {
const buildPhaseObj = this.buildPhaseObj();
const phase = buildPhaseObj[key];
if (!phase) {
return false;
}
if (!phase.shellScript) {
return false;
}
const code = JSON.parse(phase.shellScript);
return ((line instanceof RegExp ? code.search(line) : code.indexOf(line)) > -1);
}
modifyPhase(key, line, add) {
// Doesn't include line
if (!this.hasLine(key, line)) {
return;
}
// Already has add
if (add && this.hasLine(key, add)) {
return;
}
const buildPhaseObj = this.buildPhaseObj();
const phase = buildPhaseObj[key];
if (!phase) {
return;
}
if (!phase.shellScript) {
return;
}
let code = JSON.parse(phase.shellScript);
code = code.replace(line, (match) => `${add}${add === "" ? "" : match}`);
phase.shellScript = JSON.stringify(code);
}
findPhase(line) {
const buildPhaseObj = this.buildPhaseObj();
return (Object.keys(buildPhaseObj).find(key => {
return this.hasLine(key, line);
}) || "");
}
findAndRemovePhase(line) {
const buildPhaseObj = this.buildPhaseObj();
this.project.hash.project.objects.PBXShellScriptBuildPhase = Object.keys(buildPhaseObj).reduce((a, key) => {
const phase = buildPhaseObj[key];
if (!phase) {
return a;
}
if (phase.shellScript) {
const code = JSON.parse(phase.shellScript);
if (code.search(line) > -1) {
return a;
}
}
return Object.assign(Object.assign({}, a), { [key]: buildPhaseObj[key] });
}, {});
const nativeTargets = this.project.hash.project.objects.PBXNativeTarget;
this.project.hash.project.objects.PBXNativeTarget = Object.keys(nativeTargets).reduce((a, key) => {
const phase = nativeTargets[key];
if (!phase) {
return a;
}
if (phase.buildPhases) {
phase.buildPhases = phase.buildPhases.filter(({ comment }) => !comment.includes("Upload Debug Symbols to Embrace"));
}
return Object.assign(Object.assign({}, a), { [key]: phase });
}, {});
}
patch() {
fs.writeFileSync(this.path, this.writeSync());
}
writeSync() {
return this.project.writeSync();
}
addFile(groupName, path, fileType) {
const target = this.findHash(this.project.hash.project.objects.PBXNativeTarget, groupName);
const group = this.findHash(this.project.hash.project.objects.PBXGroup, groupName);
if (target && group) {
const file = this.project.addFile(path, group[0], { target: target[0] });
file.target = target[0];
file.uuid = this.project.generateUuid();
this.project.addToPbxBuildFileSection(file);
if (fileType === "resource") {
this.project.addToPbxResourcesBuildPhase(file);
}
else if (fileType === "source") {
this.project.addToPbxSourcesBuildPhase(file);
}
}
}
getBridgingHeaderName(groupName) {
const productModuleName = this.getBuildProperty(groupName, "PRODUCT_MODULE_NAME") || groupName;
return (0, exports.projectNameToBridgingHeader)(productModuleName);
}
getTargetHash(name) {
const target = this.project.pbxTargetByName(name);
return target ? target.buildConfigurationList : "";
}
getBuildProperty(groupName, propertyName) {
return this.project.getBuildProperty(propertyName, undefined, groupName);
}
updateBuildProperty(groupName, propertyName, value) {
const targetHash = this.getTargetHash(groupName);
const configurations = this.project.pbxXCBuildConfigurationSection();
const xcConfigList = this.project.pbxXCConfigurationList();
for (const configName in xcConfigList) {
if (configName !== targetHash) {
continue;
}
for (const item of xcConfigList[configName].buildConfigurations) {
const config = configurations[item.value];
if (config) {
config.buildSettings[propertyName] = value;
}
}
}
}
removeResourceFile(groupName, path) {
const target = this.findHash(this.project.hash.project.objects.PBXNativeTarget, groupName);
const group = this.findHash(this.project.hash.project.objects.PBXGroup, groupName);
if (target && group) {
const file = this.project.removeSourceFile(path, { target: target[0] }, group[0]);
this.project.removeFromPbxResourcesBuildPhase(file);
}
}
findHash(objects, groupName) {
return Object.entries(objects).find(([_, group]) => {
return group.name === groupName;
});
}
addBridgingHeader(projectName) {
return __awaiter(this, void 0, void 0, function* () {
const bridgingHeader = this.getBuildProperty(projectName, "SWIFT_OBJC_BRIDGING_HEADER");
if (bridgingHeader) {
embLogger.warn("bridging header already exists");
return true;
}
// Add the bridging header file
const filename = `${projectName}-Bridging-Header.h`;
fs.writeFileSync(osPath.join(this.path, "../../", projectName, filename), getBridgingHeaderContents());
const nameWithCaseSensitive = (0, exports.findNameWithCaseSensitiveFromPath)(this.path, projectName);
this.addFile(nameWithCaseSensitive, `${nameWithCaseSensitive}/${filename}`, "source");
this.updateBuildProperty(projectName, "SWIFT_OBJC_BRIDGING_HEADER", `"${nameWithCaseSensitive}/${filename}"`);
this.patch();
return true;
});
}
}
exports.XcodeProject = XcodeProject;
// https://developer.apple.com/documentation/swift/importing-swift-into-objective-c#Overview
const projectNameToBridgingHeader = (projectName) => {
const alphanumericOnly = projectName.replace(/\W+/g, "_");
const firstNumberReplaced = alphanumericOnly.replace(/^\d/, "_");
return `${firstNumberReplaced}-Swift.h`;
};
exports.projectNameToBridgingHeader = projectNameToBridgingHeader;
const getBridgingHeaderContents = () => {
return `//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
`;
};
const findNameWithCaseSensitiveFromPath = (path, name) => {
const pathSplitted = path.split("/");
const nameInLowerCase = name.toLocaleLowerCase();
const nameFounded = pathSplitted.find(element => element.toLocaleLowerCase() === `${nameInLowerCase}.xcodeproj`);
if (nameFounded) {
return nameFounded.replace(".xcodeproj", "");
}
return name;
};
exports.findNameWithCaseSensitiveFromPath = findNameWithCaseSensitiveFromPath;