UNPKG

@embrace-io/react-native

Version:
291 lines (290 loc) 12.7 kB
"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;