mirror of
https://github.com/expo/config-plugins.git
synced 2026-11-13 20:28:07 -10:00
feat: create react-native-branch plugin
This commit is contained in:
21 files changed
+957
No files matched your search
@@ -57,6 +57,10 @@
|
||||
],
|
||||
"@config-plugins/detox",
|
||||
"@config-plugins/react-native-adjust",
|
||||
["@config-plugins/react-native-branch", {
|
||||
"apiKey": "key_live_f9f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8",
|
||||
"appDomain": "https://bacon.app.link"
|
||||
}],
|
||||
"@config-plugins/react-native-google-cast",
|
||||
"@config-plugins/react-native-ble-plx",
|
||||
"@config-plugins/react-native-webrtc",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"@config-plugins/react-native-adjust": "^0.0.1",
|
||||
"@config-plugins/react-native-ble-plx": "^0.0.2",
|
||||
"@config-plugins/react-native-blob-util": "^0.0.0",
|
||||
"@config-plugins/react-native-branch": "^0.0.1",
|
||||
"@config-plugins/react-native-callkeep": "^0.0.1",
|
||||
"@config-plugins/react-native-dynamic-app-icon": "^0.0.2",
|
||||
"@config-plugins/react-native-google-cast": "^0.1.0",
|
||||
@@ -49,6 +50,7 @@
|
||||
"react-native-adjust": "^4.29.5",
|
||||
"react-native-blob-util": "^0.13.17",
|
||||
"react-native-callkeep": "^4.2.0",
|
||||
"react-native-branch": "^5.2.1",
|
||||
"react-native-dynamic-app-icon": "^1.1.0",
|
||||
"react-native-google-cast": "^4.1.2",
|
||||
"react-native-pdf": "^6.4.0",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// @generated by expo-module-scripts
|
||||
module.exports = require('expo-module-scripts/eslintrc.base.js');
|
||||
@@ -0,0 +1,51 @@
|
||||
# config-plugins/react-native-branch
|
||||
|
||||
Config plugin to auto configure `react-native-branch` when the native code is generated (`expo prebuild`).
|
||||
|
||||
## Expo installation
|
||||
|
||||
> Tested against Expo SDK 43
|
||||
|
||||
> This package cannot be used in the "Expo Go" app because [it requires custom native code](https://docs.expo.io/workflow/customizing/).
|
||||
> First install the package with yarn, npm, or [`expo install`](https://docs.expo.io/workflow/expo-cli/#expo-install).
|
||||
|
||||
```sh
|
||||
expo install react-native-branch @config-plugins/react-native-branch
|
||||
```
|
||||
|
||||
After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"plugins": ["@config-plugins/react-native-branch"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
|
||||
|
||||
## API
|
||||
|
||||
The plugin provides props for extra customization. Every time you change the props or plugins, you'll need to rebuild (and `prebuild`) the native app. If no extra properties are added, defaults will be used.
|
||||
|
||||
- `apiKey` (_string_): Branch API key. Required.
|
||||
- `appDomain` (_string_): App Domain for iOS. Required.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"plugins": [
|
||||
[
|
||||
"@config-plugins/react-native-branch",
|
||||
{
|
||||
"apiKey": "xxxxxx",
|
||||
"appDomain": "xxxxxx",
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require("./build/withBranch");
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export declare type ConfigData = {
|
||||
apiKey: string;
|
||||
appDomain: string;
|
||||
};
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import type { ConfigPlugin } from "@expo/config-plugins";
|
||||
import type { ConfigData } from "./types";
|
||||
declare const _default: ConfigPlugin<ConfigData>;
|
||||
export default _default;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const withBranchAndroid_1 = require("./withBranchAndroid");
|
||||
const withBranchIOS_1 = require("./withBranchIOS");
|
||||
const withBranch = (config, { apiKey, appDomain }) => {
|
||||
return config_plugins_1.withPlugins(config, [
|
||||
[withBranchIOS_1.withBranchIos, { apiKey, appDomain }],
|
||||
[withBranchAndroid_1.withBranchAndroid, { apiKey }],
|
||||
]);
|
||||
};
|
||||
let pkg = {
|
||||
name: "branch",
|
||||
};
|
||||
try {
|
||||
const branchPkg = require("react-native-branch/package.json");
|
||||
pkg = branchPkg;
|
||||
}
|
||||
catch (_a) { }
|
||||
exports.default = config_plugins_1.createRunOncePlugin(withBranch, pkg.name, pkg.version);
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { AndroidConfig } from "@expo/config-plugins";
|
||||
import type { ConfigPlugin, ExportedConfigWithProps } from "@expo/config-plugins";
|
||||
import type { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
|
||||
export declare function editMainApplication(config: ExportedConfigWithProps, action: (mainApplication: string) => string): Promise<void>;
|
||||
export declare function editProguardRules(config: ExportedConfigWithProps, action: (mainApplication: string) => string): Promise<void>;
|
||||
export declare function addBranchMainApplicationImport(src: string, packageId: string): MergeResults;
|
||||
export declare function addBranchGetAutoInstance(src: string): MergeResults;
|
||||
export declare function addBranchMainActivityImport(src: string, packageId: string): MergeResults;
|
||||
export declare function addBranchInitSession(src: string): MergeResults;
|
||||
export declare function addBranchOnNewIntent(src: string): MergeResults;
|
||||
export declare function setCustomConfigAsync(config: ExportedConfigWithProps<AndroidConfig.Manifest.AndroidManifest>, androidManifest: AndroidConfig.Manifest.AndroidManifest, branchApiKey: string): Promise<AndroidConfig.Manifest.AndroidManifest>;
|
||||
export declare const withBranchAndroid: ConfigPlugin<{
|
||||
apiKey: string;
|
||||
}>;
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.withBranchAndroid = exports.setCustomConfigAsync = exports.addBranchOnNewIntent = exports.addBranchInitSession = exports.addBranchMainActivityImport = exports.addBranchGetAutoInstance = exports.addBranchMainApplicationImport = exports.editProguardRules = exports.editMainApplication = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const assert_1 = __importDefault(require("assert"));
|
||||
async function readFileAsync(path) {
|
||||
return fs_1.default.promises.readFile(path, "utf8");
|
||||
}
|
||||
async function saveFileAsync(path, content) {
|
||||
return fs_1.default.promises.writeFile(path, content, "utf8");
|
||||
}
|
||||
// Fork of config-plugins mergeContents, but appends the contents to the end of the file.
|
||||
function appendContents({ src, newSrc, tag, comment, }) {
|
||||
const header = generateCode_1.createGeneratedHeaderComment(newSrc, tag, comment);
|
||||
if (!src.includes(header)) {
|
||||
// Ensure the old generated contents are removed.
|
||||
const sanitizedTarget = generateCode_1.removeGeneratedContents(src, tag);
|
||||
const contentsToAdd = [
|
||||
// @something
|
||||
header,
|
||||
// contents
|
||||
newSrc,
|
||||
// @end
|
||||
`${comment} @generated end ${tag}`,
|
||||
].join("\n");
|
||||
return {
|
||||
contents: (sanitizedTarget !== null && sanitizedTarget !== void 0 ? sanitizedTarget : src) + contentsToAdd,
|
||||
didMerge: true,
|
||||
didClear: !!sanitizedTarget,
|
||||
};
|
||||
}
|
||||
return { contents: src, didClear: false, didMerge: false };
|
||||
}
|
||||
async function editMainApplication(config, action) {
|
||||
var _a;
|
||||
const packageName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
|
||||
assert_1.default(packageName, "android.package must be defined");
|
||||
const mainApplicationPath = path_1.default.join(config.modRequest.platformProjectRoot, "app", "src", "main", "java", ...packageName.split("."), "MainApplication.java");
|
||||
try {
|
||||
const mainApplication = action(await readFileAsync(mainApplicationPath));
|
||||
return await saveFileAsync(mainApplicationPath, mainApplication);
|
||||
}
|
||||
catch (e) {
|
||||
config_plugins_1.WarningAggregator.addWarningAndroid("rn-branch-plugin", `Couldn't modify MainApplication.java - ${e}.`);
|
||||
}
|
||||
}
|
||||
exports.editMainApplication = editMainApplication;
|
||||
async function editProguardRules(config, action) {
|
||||
const proguardRulesPath = path_1.default.join(config.modRequest.platformProjectRoot, "app", "proguard-rules.pro");
|
||||
try {
|
||||
const proguardRules = action(await readFileAsync(proguardRulesPath));
|
||||
return await saveFileAsync(proguardRulesPath, proguardRules);
|
||||
}
|
||||
catch (e) {
|
||||
config_plugins_1.WarningAggregator.addWarningAndroid("rn-branch-plugin", `Couldn't modify proguard-rules.pro - ${e}.`);
|
||||
}
|
||||
}
|
||||
exports.editProguardRules = editProguardRules;
|
||||
function addBranchMainApplicationImport(src, packageId) {
|
||||
const newSrc = ["import io.branch.rnbranch.RNBranchModule;"];
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-import",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `package ${packageId};`,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchMainApplicationImport = addBranchMainApplicationImport;
|
||||
function addBranchGetAutoInstance(src) {
|
||||
const newSrc = [" RNBranchModule.getAutoInstance(this);"];
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-auto-instance",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /super\.onCreate\(\);/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchGetAutoInstance = addBranchGetAutoInstance;
|
||||
function addBranchMainActivityImport(src, packageId) {
|
||||
const newSrc = [
|
||||
"import android.content.Intent;",
|
||||
"import io.branch.rnbranch.*;",
|
||||
];
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-import",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `package ${packageId};`,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchMainActivityImport = addBranchMainActivityImport;
|
||||
function addBranchInitSession(src) {
|
||||
const tag = "rn-branch-init-session";
|
||||
try {
|
||||
const newSrc = [
|
||||
" RNBranchModule.initSession(getIntent().getData(), this);",
|
||||
];
|
||||
return generateCode_1.mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /super\.onStart\(\);/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code !== "ERR_NO_MATCH") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const newSrc = [
|
||||
" @Override",
|
||||
" protected void onStart() {",
|
||||
" super.onStart();",
|
||||
" RNBranchModule.initSession(getIntent().getData(), this);",
|
||||
" }",
|
||||
];
|
||||
return generateCode_1.mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `getMainComponentName`,
|
||||
offset: 3,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchInitSession = addBranchInitSession;
|
||||
function addBranchOnNewIntent(src) {
|
||||
const tag = "rn-branch-on-new-intent";
|
||||
try {
|
||||
const newSrc = [" RNBranchModule.onNewIntent(intent);"];
|
||||
return generateCode_1.mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /super\.onNewIntent\(intent\);/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code !== "ERR_NO_MATCH") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const newSrc = [
|
||||
" @Override",
|
||||
" public void onNewIntent(Intent intent) {",
|
||||
" super.onNewIntent(intent);",
|
||||
" RNBranchModule.onNewIntent(intent);",
|
||||
" }",
|
||||
];
|
||||
return generateCode_1.mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `getMainComponentName`,
|
||||
offset: 3,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchOnNewIntent = addBranchOnNewIntent;
|
||||
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow } = config_plugins_1.AndroidConfig.Manifest;
|
||||
// Splitting this function out of the mod makes it easier to test.
|
||||
async function setCustomConfigAsync(config, androidManifest, branchApiKey) {
|
||||
// Get the <application /> tag and assert if it doesn't exist.
|
||||
const mainApplication = getMainApplicationOrThrow(androidManifest);
|
||||
addMetaDataItemToMainApplication(mainApplication,
|
||||
// value for `android:name`
|
||||
"io.branch.sdk.BranchKey",
|
||||
// value for `android:value`
|
||||
branchApiKey);
|
||||
return androidManifest;
|
||||
}
|
||||
exports.setCustomConfigAsync = setCustomConfigAsync;
|
||||
const withBranchAndroid = (config, data) => {
|
||||
// Insert the branch_key into the AndroidManifest
|
||||
config = config_plugins_1.withAndroidManifest(config, async (config) => {
|
||||
// Modifiers can be async, but try to keep them fast.
|
||||
config.modResults = await setCustomConfigAsync(config, config.modResults, data.apiKey);
|
||||
return config;
|
||||
});
|
||||
// Directly edit MainApplication.java
|
||||
config = config_plugins_1.withDangerousMod(config, [
|
||||
"android",
|
||||
async (config) => {
|
||||
var _a;
|
||||
const packageName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
|
||||
assert_1.default(packageName, "android.package must be defined");
|
||||
await editMainApplication(config, (mainApplication) => {
|
||||
mainApplication = addBranchMainApplicationImport(mainApplication, packageName).contents;
|
||||
mainApplication = addBranchGetAutoInstance(mainApplication).contents;
|
||||
return mainApplication;
|
||||
});
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
// Update proguard rules directly
|
||||
config = config_plugins_1.withDangerousMod(config, [
|
||||
"android",
|
||||
async (config) => {
|
||||
await editProguardRules(config, (proguardRules) => {
|
||||
return appendContents({
|
||||
tag: "rn-branch-dont-warn",
|
||||
src: proguardRules,
|
||||
newSrc: ["-dontwarn io.branch.**"].join("\n"),
|
||||
comment: "#",
|
||||
}).contents;
|
||||
});
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
// Insert the required Branch code into MainActivity.java
|
||||
config = config_plugins_1.withMainActivity(config, (config) => {
|
||||
var _a;
|
||||
const packageName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
|
||||
assert_1.default(packageName, "android.package must be defined");
|
||||
config.modResults.contents = addBranchMainActivityImport(config.modResults.contents, packageName).contents;
|
||||
config.modResults.contents = addBranchInitSession(config.modResults.contents).contents;
|
||||
config.modResults.contents = addBranchOnNewIntent(config.modResults.contents).contents;
|
||||
return config;
|
||||
});
|
||||
return config;
|
||||
};
|
||||
exports.withBranchAndroid = withBranchAndroid;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
|
||||
import type { ConfigPlugin } from "@expo/config-plugins";
|
||||
import type { ConfigData } from "./types";
|
||||
export declare function addBranchAppDelegateImport(src: string): MergeResults;
|
||||
export declare function addBranchAppDelegateInit(src: string): MergeResults;
|
||||
export declare function addBranchAppDelegateOpenURL(src: string): MergeResults;
|
||||
export declare function addBranchAppDelegateContinueUserActivity(src: string): MergeResults;
|
||||
export declare const withBranchIos: ConfigPlugin<ConfigData>;
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.withBranchIos = exports.addBranchAppDelegateContinueUserActivity = exports.addBranchAppDelegateOpenURL = exports.addBranchAppDelegateInit = exports.addBranchAppDelegateImport = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
|
||||
function addBranchAppDelegateImport(src) {
|
||||
const newSrc = ["#import <RNBranch/RNBranch.h>"];
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-import",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /#import "AppDelegate\.h"/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchAppDelegateImport = addBranchAppDelegateImport;
|
||||
// Match against `UMModuleRegistryAdapter` (unimodules), and React Native without unimodules (Expo Modules).
|
||||
const MATCH_INIT = /(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g;
|
||||
function addBranchAppDelegateInit(src) {
|
||||
const newSrc = [];
|
||||
newSrc.push(" [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];");
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-init",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: MATCH_INIT,
|
||||
offset: 0,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchAppDelegateInit = addBranchAppDelegateInit;
|
||||
function addBranchAppDelegateOpenURL(src) {
|
||||
const newSrc = [
|
||||
" if ([RNBranch application:application openURL:url options:options]) {",
|
||||
" // do other deep link routing for the Facebook SDK, Pinterest SDK, etc",
|
||||
" }",
|
||||
];
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-open-url",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /\(UIApplication \*\)application openURL:/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchAppDelegateOpenURL = addBranchAppDelegateOpenURL;
|
||||
function addBranchAppDelegateContinueUserActivity(src) {
|
||||
const newSrc = [
|
||||
" if ([RNBranch continueUserActivity:userActivity]) {",
|
||||
" return YES;",
|
||||
" }",
|
||||
];
|
||||
return generateCode_1.mergeContents({
|
||||
tag: "rn-branch-continue-user-activity",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /\(UIApplication \*\)application continueUserActivity:/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addBranchAppDelegateContinueUserActivity = addBranchAppDelegateContinueUserActivity;
|
||||
const withBranchIos = (config, data) => {
|
||||
// Ensure object exist
|
||||
if (!config.ios) {
|
||||
config.ios = {};
|
||||
}
|
||||
// Update the infoPlist with the branch key and branch domain
|
||||
config = config_plugins_1.withInfoPlist(config, (config) => {
|
||||
config.modResults.branch_app_domain = data.appDomain;
|
||||
config.modResults.branch_key = {
|
||||
live: data.apiKey,
|
||||
};
|
||||
return config;
|
||||
});
|
||||
// Update the AppDelegate.m
|
||||
config = config_plugins_1.withAppDelegate(config, (config) => {
|
||||
config.modResults.contents = addBranchAppDelegateImport(config.modResults.contents).contents;
|
||||
config.modResults.contents = addBranchAppDelegateInit(config.modResults.contents).contents;
|
||||
config.modResults.contents = addBranchAppDelegateOpenURL(config.modResults.contents).contents;
|
||||
config.modResults.contents = addBranchAppDelegateContinueUserActivity(config.modResults.contents).contents;
|
||||
return config;
|
||||
});
|
||||
return config;
|
||||
};
|
||||
exports.withBranchIos = withBranchIos;
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('expo-module-scripts/jest-preset-plugin');
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@config-plugins/react-native-branch",
|
||||
"version": "0.0.1",
|
||||
"description": "Config plugin to auto configure react-native-branch on prebuild",
|
||||
"main": "build/withBranch.js",
|
||||
"types": "build/withBranch.d.ts",
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"build",
|
||||
"app.plugin.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "expo-module build",
|
||||
"clean": "expo-module clean",
|
||||
"lint": "expo-module lint",
|
||||
"test": "expo-module test",
|
||||
"prepare": "expo-module prepare",
|
||||
"prepublishOnly": "expo-module prepublishOnly",
|
||||
"expo-module": "expo-module"
|
||||
},
|
||||
"keywords": [
|
||||
"expo",
|
||||
"react-native",
|
||||
"react-native-branch"
|
||||
],
|
||||
"jest": {
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type ConfigData = {
|
||||
apiKey: string;
|
||||
appDomain: string;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createRunOncePlugin, withPlugins } from "@expo/config-plugins";
|
||||
import type { ConfigPlugin } from "@expo/config-plugins";
|
||||
|
||||
import { withBranchAndroid } from "./withBranchAndroid";
|
||||
import { withBranchIos } from "./withBranchIOS";
|
||||
import type { ConfigData } from "./types";
|
||||
|
||||
const withBranch: ConfigPlugin<ConfigData> = (config, { apiKey, appDomain }) => {
|
||||
return withPlugins(
|
||||
config,
|
||||
[
|
||||
[withBranchIos, { apiKey, appDomain }],
|
||||
[withBranchAndroid, { apiKey }],
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
let pkg: { name: string; version?: string } = {
|
||||
name: "branch",
|
||||
};
|
||||
try {
|
||||
const branchPkg = require("react-native-branch/package.json");
|
||||
pkg = branchPkg;
|
||||
} catch {}
|
||||
|
||||
export default createRunOncePlugin(withBranch, pkg.name, pkg.version);
|
||||
@@ -0,0 +1,333 @@
|
||||
import {
|
||||
AndroidConfig,
|
||||
WarningAggregator,
|
||||
withAndroidManifest,
|
||||
withDangerousMod,
|
||||
withMainActivity,
|
||||
} from "@expo/config-plugins";
|
||||
import type {
|
||||
ConfigPlugin,
|
||||
ExportedConfigWithProps,
|
||||
} from "@expo/config-plugins";
|
||||
import {
|
||||
createGeneratedHeaderComment,
|
||||
mergeContents,
|
||||
removeGeneratedContents,
|
||||
} from "@expo/config-plugins/build/utils/generateCode";
|
||||
import type { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import assert from "assert";
|
||||
|
||||
async function readFileAsync(path: string) {
|
||||
return fs.promises.readFile(path, "utf8");
|
||||
}
|
||||
|
||||
async function saveFileAsync(path: string, content: string) {
|
||||
return fs.promises.writeFile(path, content, "utf8");
|
||||
}
|
||||
|
||||
// Fork of config-plugins mergeContents, but appends the contents to the end of the file.
|
||||
function appendContents({
|
||||
src,
|
||||
newSrc,
|
||||
tag,
|
||||
comment,
|
||||
}: {
|
||||
src: string;
|
||||
newSrc: string;
|
||||
tag: string;
|
||||
comment: string;
|
||||
}): MergeResults {
|
||||
const header = createGeneratedHeaderComment(newSrc, tag, comment);
|
||||
if (!src.includes(header)) {
|
||||
// Ensure the old generated contents are removed.
|
||||
const sanitizedTarget = removeGeneratedContents(src, tag);
|
||||
const contentsToAdd = [
|
||||
// @something
|
||||
header,
|
||||
// contents
|
||||
newSrc,
|
||||
// @end
|
||||
`${comment} @generated end ${tag}`,
|
||||
].join("\n");
|
||||
|
||||
return {
|
||||
contents: (sanitizedTarget ?? src) + contentsToAdd,
|
||||
didMerge: true,
|
||||
didClear: !!sanitizedTarget,
|
||||
};
|
||||
}
|
||||
|
||||
return { contents: src, didClear: false, didMerge: false };
|
||||
}
|
||||
|
||||
export async function editMainApplication(
|
||||
config: ExportedConfigWithProps,
|
||||
action: (mainApplication: string) => string
|
||||
) {
|
||||
const packageName = config.android?.package;
|
||||
assert(packageName, "android.package must be defined");
|
||||
|
||||
const mainApplicationPath = path.join(
|
||||
config.modRequest.platformProjectRoot,
|
||||
"app",
|
||||
"src",
|
||||
"main",
|
||||
"java",
|
||||
...packageName.split("."),
|
||||
"MainApplication.java"
|
||||
);
|
||||
|
||||
try {
|
||||
const mainApplication = action(await readFileAsync(mainApplicationPath));
|
||||
return await saveFileAsync(mainApplicationPath, mainApplication);
|
||||
} catch (e) {
|
||||
WarningAggregator.addWarningAndroid(
|
||||
"rn-branch-plugin",
|
||||
`Couldn't modify MainApplication.java - ${e}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function editProguardRules(
|
||||
config: ExportedConfigWithProps,
|
||||
action: (mainApplication: string) => string
|
||||
) {
|
||||
const proguardRulesPath = path.join(
|
||||
config.modRequest.platformProjectRoot,
|
||||
"app",
|
||||
"proguard-rules.pro"
|
||||
);
|
||||
try {
|
||||
const proguardRules = action(await readFileAsync(proguardRulesPath));
|
||||
return await saveFileAsync(proguardRulesPath, proguardRules);
|
||||
} catch (e) {
|
||||
WarningAggregator.addWarningAndroid(
|
||||
"rn-branch-plugin",
|
||||
`Couldn't modify proguard-rules.pro - ${e}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function addBranchMainApplicationImport(
|
||||
src: string,
|
||||
packageId: string
|
||||
): MergeResults {
|
||||
const newSrc = ["import io.branch.rnbranch.RNBranchModule;"];
|
||||
|
||||
return mergeContents({
|
||||
tag: "rn-branch-import",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `package ${packageId};`,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export function addBranchGetAutoInstance(src: string): MergeResults {
|
||||
const newSrc = [" RNBranchModule.getAutoInstance(this);"];
|
||||
|
||||
return mergeContents({
|
||||
tag: "rn-branch-auto-instance",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /super\.onCreate\(\);/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export function addBranchMainActivityImport(
|
||||
src: string,
|
||||
packageId: string
|
||||
): MergeResults {
|
||||
const newSrc = [
|
||||
"import android.content.Intent;",
|
||||
"import io.branch.rnbranch.*;",
|
||||
];
|
||||
|
||||
return mergeContents({
|
||||
tag: "rn-branch-import",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `package ${packageId};`,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export function addBranchInitSession(src: string): MergeResults {
|
||||
const tag = "rn-branch-init-session";
|
||||
|
||||
try {
|
||||
const newSrc = [
|
||||
" RNBranchModule.initSession(getIntent().getData(), this);",
|
||||
];
|
||||
|
||||
return mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /super\.onStart\(\);/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.code !== "ERR_NO_MATCH") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const newSrc = [
|
||||
" @Override",
|
||||
" protected void onStart() {",
|
||||
" super.onStart();",
|
||||
" RNBranchModule.initSession(getIntent().getData(), this);",
|
||||
" }",
|
||||
];
|
||||
return mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `getMainComponentName`,
|
||||
offset: 3,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export function addBranchOnNewIntent(src: string): MergeResults {
|
||||
const tag = "rn-branch-on-new-intent";
|
||||
|
||||
try {
|
||||
const newSrc = [" RNBranchModule.onNewIntent(intent);"];
|
||||
|
||||
return mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /super\.onNewIntent\(intent\);/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.code !== "ERR_NO_MATCH") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const newSrc = [
|
||||
" @Override",
|
||||
" public void onNewIntent(Intent intent) {",
|
||||
" super.onNewIntent(intent);",
|
||||
" RNBranchModule.onNewIntent(intent);",
|
||||
" }",
|
||||
];
|
||||
return mergeContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: `getMainComponentName`,
|
||||
offset: 3,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow } =
|
||||
AndroidConfig.Manifest;
|
||||
|
||||
// Splitting this function out of the mod makes it easier to test.
|
||||
export async function setCustomConfigAsync(
|
||||
config: ExportedConfigWithProps<AndroidConfig.Manifest.AndroidManifest>,
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
branchApiKey: string
|
||||
) {
|
||||
// Get the <application /> tag and assert if it doesn't exist.
|
||||
const mainApplication = getMainApplicationOrThrow(androidManifest);
|
||||
|
||||
addMetaDataItemToMainApplication(
|
||||
mainApplication,
|
||||
// value for `android:name`
|
||||
"io.branch.sdk.BranchKey",
|
||||
// value for `android:value`
|
||||
branchApiKey
|
||||
);
|
||||
|
||||
return androidManifest;
|
||||
}
|
||||
|
||||
export const withBranchAndroid: ConfigPlugin<{ apiKey: string }> = (
|
||||
config,
|
||||
data
|
||||
) => {
|
||||
// Insert the branch_key into the AndroidManifest
|
||||
config = withAndroidManifest(config, async (config) => {
|
||||
// Modifiers can be async, but try to keep them fast.
|
||||
config.modResults = await setCustomConfigAsync(
|
||||
config,
|
||||
config.modResults,
|
||||
data.apiKey
|
||||
);
|
||||
return config;
|
||||
});
|
||||
|
||||
// Directly edit MainApplication.java
|
||||
config = withDangerousMod(config, [
|
||||
"android",
|
||||
async (config) => {
|
||||
const packageName = config.android?.package;
|
||||
assert(packageName, "android.package must be defined");
|
||||
|
||||
await editMainApplication(config, (mainApplication) => {
|
||||
mainApplication = addBranchMainApplicationImport(
|
||||
mainApplication,
|
||||
packageName
|
||||
).contents;
|
||||
mainApplication = addBranchGetAutoInstance(mainApplication).contents;
|
||||
|
||||
return mainApplication;
|
||||
});
|
||||
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
|
||||
// Update proguard rules directly
|
||||
config = withDangerousMod(config, [
|
||||
"android",
|
||||
async (config) => {
|
||||
await editProguardRules(config, (proguardRules) => {
|
||||
return appendContents({
|
||||
tag: "rn-branch-dont-warn",
|
||||
src: proguardRules,
|
||||
newSrc: ["-dontwarn io.branch.**"].join("\n"),
|
||||
comment: "#",
|
||||
}).contents;
|
||||
});
|
||||
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
|
||||
// Insert the required Branch code into MainActivity.java
|
||||
config = withMainActivity(config, (config) => {
|
||||
const packageName = config.android?.package;
|
||||
assert(packageName, "android.package must be defined");
|
||||
|
||||
config.modResults.contents = addBranchMainActivityImport(
|
||||
config.modResults.contents,
|
||||
packageName
|
||||
).contents;
|
||||
config.modResults.contents = addBranchInitSession(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
config.modResults.contents = addBranchOnNewIntent(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
import { withAppDelegate, withInfoPlist } from "@expo/config-plugins";
|
||||
import { mergeContents, MergeResults } from "@expo/config-plugins/build/utils/generateCode";
|
||||
import type { ConfigPlugin } from "@expo/config-plugins";
|
||||
import type { ConfigData } from "./types";
|
||||
|
||||
export function addBranchAppDelegateImport(src: string): MergeResults {
|
||||
const newSrc = ["#import <RNBranch/RNBranch.h>"];
|
||||
return mergeContents({
|
||||
tag: "rn-branch-import",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /#import "AppDelegate\.h"/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
// Match against `UMModuleRegistryAdapter` (unimodules), and React Native without unimodules (Expo Modules).
|
||||
const MATCH_INIT =
|
||||
/(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g;
|
||||
|
||||
export function addBranchAppDelegateInit(src: string): MergeResults {
|
||||
const newSrc = [];
|
||||
newSrc.push(
|
||||
" [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];"
|
||||
);
|
||||
|
||||
return mergeContents({
|
||||
tag: "rn-branch-init",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: MATCH_INIT,
|
||||
offset: 0,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export function addBranchAppDelegateOpenURL(src: string): MergeResults {
|
||||
const newSrc = [
|
||||
" if ([RNBranch application:application openURL:url options:options]) {",
|
||||
" // do other deep link routing for the Facebook SDK, Pinterest SDK, etc",
|
||||
" }",
|
||||
];
|
||||
|
||||
return mergeContents({
|
||||
tag: "rn-branch-open-url",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /\(UIApplication \*\)application openURL:/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export function addBranchAppDelegateContinueUserActivity(src: string): MergeResults {
|
||||
const newSrc = [
|
||||
" if ([RNBranch continueUserActivity:userActivity]) {",
|
||||
" return YES;",
|
||||
" }",
|
||||
];
|
||||
|
||||
return mergeContents({
|
||||
tag: "rn-branch-continue-user-activity",
|
||||
src,
|
||||
newSrc: newSrc.join("\n"),
|
||||
anchor: /\(UIApplication \*\)application continueUserActivity:/,
|
||||
offset: 1,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
export const withBranchIos: ConfigPlugin<ConfigData> = (config, data) =>{
|
||||
// Ensure object exist
|
||||
if (!config.ios) {
|
||||
config.ios = {};
|
||||
}
|
||||
|
||||
// Update the infoPlist with the branch key and branch domain
|
||||
config = withInfoPlist(config, (config) => {
|
||||
config.modResults.branch_app_domain = data.appDomain;
|
||||
config.modResults.branch_key = {
|
||||
live: data.apiKey,
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// Update the AppDelegate.m
|
||||
config = withAppDelegate(config, (config) => {
|
||||
config.modResults.contents = addBranchAppDelegateImport(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
config.modResults.contents = addBranchAppDelegateInit(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
config.modResults.contents = addBranchAppDelegateOpenURL(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
config.modResults.contents = addBranchAppDelegateContinueUserActivity(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "expo-module-scripts/tsconfig.plugin",
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
|
||||
}
|
||||
@@ -14749,6 +14749,11 @@ react-native-blob-util@^0.13.17:
|
||||
base-64 "0.1.0"
|
||||
glob "^7.1.6"
|
||||
|
||||
react-native-branch@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-branch/-/react-native-branch-5.2.1.tgz#72329419365822708533ae93b31de0547e301b59"
|
||||
integrity sha512-SDGbIh+axaHscoZ+vXAQQ+POcwf8tF2jQDtdxRE7JHJ/B9pqvChqTicllxwgyrXhZ0cj4TtpvLaD9jQ2ktnNyw==
|
||||
|
||||
react-native-callkeep@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-callkeep/-/react-native-callkeep-4.2.0.tgz#84f20eaf07a4b3494502bf9156dbdbf4b7617122"
|
||||
|
||||
Reference in new issue
Block a user