mirror of
https://github.com/expo/config-plugins.git
synced 2026-11-13 20:28:07 -10:00
Compare commits
79
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
754fb08327 | ||
|
|
7a7b88b735 | ||
|
|
954cc4c189 | ||
|
|
dcf61a9b95 | ||
|
|
4a7d707b20 | ||
|
|
eedb489416 | ||
|
|
3091016389 | ||
|
|
4f9e566e4b | ||
|
|
027a1fdb1e | ||
|
|
ca6b70dc7a | ||
|
|
b96cb784b9 | ||
|
|
d44a0513a3 | ||
|
|
f396c0eca2 | ||
|
|
b72c445561 | ||
|
|
b6d7a269c3 | ||
|
|
b1c48798a2 | ||
|
|
c0caaa117e | ||
|
|
24b6abbf63 | ||
|
|
b46f25e204 | ||
|
|
7d5e4a3083 | ||
|
|
581dd45715 | ||
|
|
a7e4206ffa | ||
|
|
05fe7bf107 | ||
|
|
fa107895b6 | ||
|
|
d31b0273cf | ||
|
|
cd9a188413 | ||
|
|
4c1607a2cf | ||
|
|
3a7a22be0e | ||
|
|
978448646d | ||
|
|
3703b47b53 | ||
|
|
e370254d10 | ||
|
|
2f358c25e8 | ||
|
|
5caee7a24c | ||
|
|
3ac42ac2fa | ||
|
|
d2eb511e1d | ||
|
|
f62a941bc6 | ||
|
|
d784d14486 | ||
|
|
7b0d0225f9 | ||
|
|
c1d25701ca | ||
|
|
529410be0f | ||
|
|
04ef242347 | ||
|
|
fe00996dbd | ||
|
|
ba84473db8 | ||
|
|
359cbb455d | ||
|
|
909db3c158 | ||
|
|
0aaf6d07d6 | ||
|
|
81857f8938 | ||
|
|
dc86073608 | ||
|
|
8dd7006769 | ||
|
|
f62f4f59ba | ||
|
|
63257cfa20 | ||
|
|
b6b9ce09c1 | ||
|
|
4c3cd1413b | ||
|
|
5833dd9ea3 | ||
|
|
49448a8974 | ||
|
|
780c27319d | ||
|
|
e3acf06968 | ||
|
|
7cce4a464e | ||
|
|
d214964657 | ||
|
|
3cd73f32a2 | ||
|
|
f96f9bdc73 | ||
|
|
f0d957456e | ||
|
|
c2a78787e8 | ||
|
|
bd5710b412 | ||
|
|
f15f5fc7ed | ||
|
|
2546094be7 | ||
|
|
38458d2c9f | ||
|
|
619b2fb02c | ||
|
|
5359120dbd | ||
|
|
ed6b3a0ec0 | ||
|
|
efd3284272 | ||
|
|
46bc48726c | ||
|
|
ff47e492e4 | ||
|
|
be969274b9 | ||
|
|
359c1cfcb5 | ||
|
|
6c3fc5dff7 | ||
|
|
99d3927680 | ||
|
|
55482dae5f | ||
|
|
395599abfa |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
packages/*/build/** -diff linguist-generated
|
||||
packages/*/plugin/build/** -diff linguist-generated
|
||||
packages/@*/*/build/** -diff linguist-generated
|
||||
@@ -0,0 +1,155 @@
|
||||
name: E2E Test
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- .github/workflows/ios.yml
|
||||
- apps/app/**
|
||||
- packages/**
|
||||
- yarn.lock
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/ios.yml
|
||||
- apps/app/**
|
||||
- packages/**
|
||||
- yarn.lock
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
ios:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: 👀 Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: 🍺 Install required tools
|
||||
run: |
|
||||
brew tap wix/brew
|
||||
brew install applesimutils
|
||||
brew install watchman
|
||||
echo "$(pwd)/bin" >> $GITHUB_PATH
|
||||
- name: 💎 Setup Ruby and install gems
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
- name: ♻️ Restore workspace node modules
|
||||
uses: actions/cache@v2
|
||||
id: node-modules-cache
|
||||
with:
|
||||
path: |
|
||||
# See "workspaces" → "packages" in the root package.json for the source of truth of
|
||||
# which node_modules are affected by the root yarn.lock
|
||||
node_modules
|
||||
apps/*/node_modules
|
||||
packages/*/node_modules
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: 🧶 Install node modules in root dir
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: ♻️ Restore apps/app/ios/Pods from cache
|
||||
uses: actions/cache@v2
|
||||
id: pods-cache
|
||||
with:
|
||||
path: "apps/app/ios/Pods"
|
||||
key: ${{ runner.os }}-app-pods-${{ hashFiles('apps/app/ios/Podfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-app-pods-
|
||||
- run: yarn global add expo-cli
|
||||
- name: 🔨 Prebuild
|
||||
run: expo prebuild -p ios
|
||||
working-directory: apps/app
|
||||
- name: 🕵️ Debug CocoaPods lockfiles
|
||||
run: git diff Podfile.lock Pods/Manifest.lock
|
||||
working-directory: apps/app/ios
|
||||
continue-on-error: true
|
||||
- name: ⚛️ Display React Native config
|
||||
run: yarn react-native config
|
||||
working-directory: apps/app
|
||||
- name: 🌳 Display pod environment
|
||||
run: pod env
|
||||
working-directory: apps/app/ios
|
||||
- name: 🥥 Install pods in apps/app/ios
|
||||
run: pod install
|
||||
if: steps.pods-cache.outputs.cache-hit != 'true' || hashFiles('apps/app/ios/Podfile.lock') != hashFiles('apps/app/ios/Pods/Manifest.lock')
|
||||
working-directory: apps/app/ios
|
||||
- name: Clean Detox
|
||||
run: yarn detox:clean
|
||||
working-directory: apps/app
|
||||
- name: Build iOS project for Detox
|
||||
run: yarn build:ios-release
|
||||
working-directory: apps/app
|
||||
timeout-minutes: 30
|
||||
- name: Run tests
|
||||
run: yarn test:ios-release
|
||||
working-directory: apps/app
|
||||
- name: Store images of build failures
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-artifacts
|
||||
path: apps/app/artifacts
|
||||
|
||||
android:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: 👀 Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: 🍺 Install required tools
|
||||
run: |
|
||||
brew install watchman
|
||||
echo "$(pwd)/bin" >> $GITHUB_PATH
|
||||
- name: ♻️ Restore workspace node modules
|
||||
uses: actions/cache@v2
|
||||
id: node-modules-cache
|
||||
with:
|
||||
path: |
|
||||
# See "workspaces" → "packages" in the root package.json for the source of truth of
|
||||
# which node_modules are affected by the root yarn.lock
|
||||
node_modules
|
||||
apps/*/node_modules
|
||||
packages/*/node_modules
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: 🧶 Install node modules in root dir
|
||||
run: yarn install --frozen-lockfile
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-v20210625-${{ hashFiles('android/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-v20210625
|
||||
- run: yarn global add expo-cli
|
||||
- name: 🔨 Prebuild
|
||||
run: expo prebuild -p android
|
||||
working-directory: apps/app
|
||||
- name: ⚛️ Display React Native config
|
||||
run: yarn react-native config
|
||||
working-directory: apps/app
|
||||
- name: Clean Detox
|
||||
run: yarn detox:clean
|
||||
working-directory: apps/app
|
||||
- name: Build Android project for Detox
|
||||
run: yarn build:android-release
|
||||
working-directory: apps/app
|
||||
timeout-minutes: 35
|
||||
env:
|
||||
GRADLE_OPTS: "-Dorg.gradle.internal.http.connectionTimeout=180000 -Dorg.gradle.internal.http.socketTimeout=180000 -Dorg.gradle.internal.network.retry.max.attempts=18 -Dorg.gradle.internal.network.retry.initial.backOff=2000"
|
||||
- name: Run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 29
|
||||
avd-name: app
|
||||
script: yarn test:android-release
|
||||
working-directory: ./apps/app
|
||||
- name: Store images of build failures
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-artifacts
|
||||
path: apps/app/artifacts
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Test Packages
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["16", "17"]
|
||||
name: Build with Node ${{ matrix.node }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- run: yarn lerna run prepare --stream
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: "*"
|
||||
key: v2-${{ github.sha }}-${{ matrix.node }}
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: ["16", "17"]
|
||||
package: [
|
||||
# Manually add packages here
|
||||
detox,
|
||||
ffmpeg-kit-react-native,
|
||||
react-native-ble-plx,
|
||||
react-native-blob-util,
|
||||
react-native-google-cast,
|
||||
react-native-pdf,
|
||||
# react-native-quick-actions,
|
||||
]
|
||||
name: Test ${{ matrix.package }} on Node ${{ matrix.node }}
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: "*"
|
||||
key: v2-${{ github.sha }}-${{ matrix.node }}
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- name: Test ${{ matrix.package }}
|
||||
run: cd packages/${{ matrix.package }} && yarn test
|
||||
env:
|
||||
CI: true
|
||||
EXPO_DEBUG: true
|
||||
@@ -0,0 +1 @@
|
||||
2.7.0
|
||||
+9
-7
@@ -12,7 +12,13 @@ web-build/
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# @generated expo-cli sync-2138f1e3e130677ea10ea873f6d498e3890e677b
|
||||
|
||||
/artifacts
|
||||
|
||||
/scripts/start.command
|
||||
/ios
|
||||
/android
|
||||
# @generated expo-cli sync-e7dcf75f4e856f7b6f3239b3f3a7dd614ee755a8
|
||||
# The following patterns were generated by expo-cli
|
||||
|
||||
# OSX
|
||||
@@ -69,10 +75,6 @@ buck-out/
|
||||
# Expo
|
||||
.expo/
|
||||
web-build/
|
||||
dist/
|
||||
|
||||
# @end expo-cli
|
||||
|
||||
/artifacts
|
||||
|
||||
/ios
|
||||
/android
|
||||
# @end expo-cli
|
||||
+4
-3
@@ -1,7 +1,8 @@
|
||||
// export { default } from './src/react-native-quick-actions/App';
|
||||
export { default } from './src/react-native-dynamic-app-icon/App';
|
||||
// export { default } from './src/react-native-dynamic-app-icon/App';
|
||||
// export { default } from './src/react-native-google-cast/App';
|
||||
// export { default } from './src/detox/App';
|
||||
export { default } from './src/detox/App';
|
||||
// export { default } from './src/react-native-ble-plx/App';
|
||||
// export { default } from './src/react-native-webrtc/App';
|
||||
// export { default } from './src/android-jsc-intl/App';
|
||||
// export { default } from './src/android-jsc-intl/App';
|
||||
// export { default } from './src/ffmpeg-kit-react-native/App';
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = ({ config }) => {
|
||||
if (!config.extra) config.extra = {};
|
||||
// Expose CI env variables to the app
|
||||
config.extra.CI = process.env.CI;
|
||||
return config;
|
||||
};
|
||||
+1
-47
@@ -31,53 +31,7 @@
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
[
|
||||
"@config-plugins/react-native-dynamic-app-icon",
|
||||
[
|
||||
"./assets/icons/spring.png",
|
||||
"./assets/icons/summer.png",
|
||||
"./assets/icons/autumn.png",
|
||||
"./assets/icons/fall.png",
|
||||
"./assets/icons/solstice.png"
|
||||
]
|
||||
],
|
||||
[
|
||||
"@config-plugins/react-native-quick-actions",
|
||||
[
|
||||
{
|
||||
"title": "Take Photo",
|
||||
"type": "photo",
|
||||
"iconType": "UIApplicationShortcutIconTypeCapturePhoto"
|
||||
}
|
||||
]
|
||||
],
|
||||
"@config-plugins/detox",
|
||||
"@config-plugins/react-native-google-cast",
|
||||
"@config-plugins/react-native-ble-plx",
|
||||
"@config-plugins/react-native-webrtc",
|
||||
"@config-plugins/react-native-callkeep",
|
||||
"@config-plugins/android-jsc-intl",
|
||||
[
|
||||
"@config-plugins/ios-stickers",
|
||||
{
|
||||
"name": "Stickers",
|
||||
"icon": "./assets/imessage-icon.png",
|
||||
"columns": 4,
|
||||
"stickers": [
|
||||
"./assets/stickers/annoyed.png",
|
||||
"./assets/stickers/cuddle.png",
|
||||
"./assets/stickers/crazy.png",
|
||||
"./assets/stickers/cant.png",
|
||||
"./assets/stickers/donno.png",
|
||||
"./assets/stickers/cozzy.png",
|
||||
"./assets/stickers/dork.png",
|
||||
"./assets/stickers/focus.png",
|
||||
"./assets/stickers/avocool.png",
|
||||
"./assets/stickers/coder-girl.png",
|
||||
"./assets/stickers/teapot.png"
|
||||
]
|
||||
}
|
||||
]
|
||||
"@config-plugins/detox"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const iosName = "app";
|
||||
const emulator = "Pixel_3a_API_30";
|
||||
const simulator = "iPhone 11 Pro";
|
||||
const emulator = process.env.CI ? "app" : "Pixel_3a_API_30";
|
||||
const simulator = "iPhone 11";
|
||||
const derivedDataPath = "ios/build";
|
||||
const sdk = "iphonesimulator";
|
||||
|
||||
@@ -17,18 +17,18 @@ module.exports = {
|
||||
"ios.release": {
|
||||
type: "ios.app",
|
||||
binaryPath: `${derivedDataPath}/Build/Products/Release-${sdk}/${iosName}.app`,
|
||||
build: `export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/${iosName}.xcworkspace -scheme ${iosName} -configuration Release -sdk ${sdk} -derivedDataPath ${derivedDataPath} | npx excpretty ./`,
|
||||
build: `./scripts/build-detox-ios.sh ${iosName} Release YES`,
|
||||
},
|
||||
"ios.debug": {
|
||||
type: "ios.app",
|
||||
binaryPath: `${derivedDataPath}/Build/Products/Debug-${sdk}/${iosName}.app`,
|
||||
build: `xcodebuild -workspace ios/${iosName}.xcworkspace -scheme ${iosName} -configuration Debug -sdk ${sdk} -derivedDataPath ${derivedDataPath} | npx excpretty ./`,
|
||||
build: `./scripts/start-metro.sh && ./scripts/build-detox-ios.sh ${iosName} Debug`,
|
||||
},
|
||||
"android.debug": {
|
||||
type: "android.apk",
|
||||
binaryPath: "android/app/build/outputs/apk/debug/app-debug.apk",
|
||||
build:
|
||||
"pushd android; ./gradlew app:assembleDebug app:assembleAndroidTest -DtestBuildType=debug; popd",
|
||||
"./scripts/start-metro.sh && pushd android; ./gradlew app:assembleDebug app:assembleAndroidTest -DtestBuildType=debug; popd",
|
||||
},
|
||||
"android.release": {
|
||||
type: "android.apk",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { by, device, element, expect } from "detox";
|
||||
import { device } from "detox";
|
||||
import * as path from "path";
|
||||
|
||||
import { createCapture } from "../lib";
|
||||
@@ -6,7 +6,10 @@ import { createCapture } from "../lib";
|
||||
const projectRoot = path.resolve(__dirname, "../../");
|
||||
|
||||
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html
|
||||
const languages = ["en-US", "nl-NL", "zh-CN"];
|
||||
const languages = [
|
||||
"en-US",
|
||||
// "nl-NL", "zh-CN"
|
||||
];
|
||||
|
||||
for (const locale of languages) {
|
||||
const { captureAsync, resetAppAsync } = createCapture({
|
||||
@@ -21,18 +24,8 @@ for (const locale of languages) {
|
||||
await device.reloadReactNative();
|
||||
});
|
||||
|
||||
it("should have welcome screen", async () => {
|
||||
await expect(element(by.id("welcome"))).toBeVisible();
|
||||
await captureAsync();
|
||||
});
|
||||
|
||||
xit("should show hello screen after tap", async () => {
|
||||
await element(by.id("hello_button")).tap();
|
||||
await captureAsync();
|
||||
});
|
||||
|
||||
xit("should show world screen after tap", async () => {
|
||||
await element(by.id("world_button")).tap();
|
||||
it("should load app", async () => {
|
||||
// await expect(element(by.id("welcome"))).toBeVisible();
|
||||
await captureAsync();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,10 +4,19 @@ import * as path from "path";
|
||||
import { setStatusBar } from "./setStatusBar";
|
||||
|
||||
export function getOutputDir(projectRoot: string, locale: string) {
|
||||
return path.resolve(
|
||||
projectRoot,
|
||||
`meta/screenshots/${device.getPlatform()}/${locale}/`
|
||||
);
|
||||
const store = device.getPlatform() === "ios" ? "apple" : "google";
|
||||
return path.resolve(projectRoot, `merch/${store}/preview/${locale}/`);
|
||||
}
|
||||
|
||||
function resolveName(str: string) {
|
||||
if (str && device.getPlatform() === "ios" && typeof str === "string") {
|
||||
// Convert a name like: `E9EA2214-6B88-4223-9872-8232168088FB {"type":"iPhone 12 Pro Max"}`
|
||||
// to `iPhone 12 Pro Max`
|
||||
// Name is important for screenshot sorting
|
||||
const match = str.match(/"type"\s?:\s?"(.*)"}/i)?.[1];
|
||||
return match || str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export async function takeScreenshotAsync({
|
||||
@@ -27,8 +36,9 @@ export async function takeScreenshotAsync({
|
||||
if (index == null) {
|
||||
index = fs.readdirSync(outputDir).length;
|
||||
}
|
||||
const deviceName = resolveName(device.name);
|
||||
|
||||
const name = `APP_${device.name}_${index}`.toUpperCase();
|
||||
const name = `APP_${deviceName}-${index}`;
|
||||
const imgPath = path.resolve(outputDir, `${name}.png`);
|
||||
const imagePath = await device.takeScreenshot(name);
|
||||
await fs.promises.copyFile(imagePath, imgPath);
|
||||
@@ -47,10 +57,10 @@ export function createCapture({
|
||||
locale,
|
||||
});
|
||||
|
||||
const clearAsync = () => {
|
||||
const clearAsync = async () => {
|
||||
// Clear old screen shots
|
||||
const outputDir = getOutputDir(projectRoot, locale);
|
||||
fs.rmdirSync(outputDir, { recursive: true });
|
||||
await fs.promises.rmdir(outputDir, { recursive: true }).catch(() => null);
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -58,7 +68,7 @@ export function createCapture({
|
||||
clearAsync,
|
||||
async resetAppAsync() {
|
||||
// Clear old screen shots
|
||||
await clearAsync();
|
||||
// await clearAsync();
|
||||
// configure status bars
|
||||
|
||||
setStatusBar();
|
||||
|
||||
+10
-1
@@ -1,7 +1,16 @@
|
||||
import "react-native-gesture-handler";
|
||||
import { registerRootComponent } from "expo";
|
||||
|
||||
import App from "./App";
|
||||
import Constants from "expo-constants";
|
||||
|
||||
let App;
|
||||
// Only use the Detox test in CI
|
||||
if ((Constants.manifest.extra || {}).CI) {
|
||||
App = require("./src/detox/App").default;
|
||||
} else {
|
||||
App = require("./App").default;
|
||||
}
|
||||
// import App from "./App";
|
||||
|
||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -1,3 +1,6 @@
|
||||
const { createMetroConfiguration } = require('expo-yarn-workspaces');
|
||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
const { getDefaultConfig } = require("@expo/metro-config");
|
||||
|
||||
module.exports = createMetroConfiguration(__dirname);
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
module.exports = config;
|
||||
+25
-67
@@ -3,6 +3,7 @@
|
||||
"main": "./index.js",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"detox:clean": "detox clean-framework-cache && detox build-framework-cache",
|
||||
"build:ios": "detox build -c ios.sim.debug",
|
||||
"test:ios": "detox test -c ios.sim.debug",
|
||||
"e2e:ios": "npm run build:ios && npm run test:ios",
|
||||
@@ -13,89 +14,46 @@
|
||||
"test:android": "detox test -c android.emu.debug",
|
||||
"e2e:android": "npm run build:android && npm run test:android",
|
||||
"build:android-release": "detox build -c android.emu.release",
|
||||
"test:android-release": "detox test -c android.emu.release",
|
||||
"test:android-release": "detox test -c android.emu.release --headless",
|
||||
"e2e:android-release": "npm run build:android-release && npm run test:android-release",
|
||||
"clean:android": "pushd android && ./gradlew clean && popd",
|
||||
"start": "expo start --dev-client",
|
||||
"ios": "expo run:ios",
|
||||
"android": "expo run:android",
|
||||
"postinstall": "expo-yarn-workspaces postinstall"
|
||||
},
|
||||
"expo-yarn-workspaces": {
|
||||
"symlinks": [
|
||||
"detox",
|
||||
"expo-constants",
|
||||
"expo-camera",
|
||||
"expo-updates",
|
||||
"react-native-callkeep",
|
||||
"react-native-webrtc",
|
||||
"react-native-quick-actions"
|
||||
]
|
||||
},
|
||||
"react-native-unimodules": {
|
||||
"android": {
|
||||
"modulesPaths": [
|
||||
"../../../../",
|
||||
"../../../../packages"
|
||||
],
|
||||
"configuration": "api",
|
||||
"target": "react-native"
|
||||
},
|
||||
"ios": {
|
||||
"modules_paths": [
|
||||
"../../../",
|
||||
"../../../packages"
|
||||
],
|
||||
"flags": {
|
||||
"inhibit_warnings": false
|
||||
}
|
||||
}
|
||||
"android": "expo run:android"
|
||||
},
|
||||
"dependencies": {
|
||||
"@config-plugins/android-jsc-intl": "^0.0.0",
|
||||
"@config-plugins/detox": "^0.0.0",
|
||||
"@config-plugins/ios-stickers": "^0.0.2",
|
||||
"@config-plugins/react-native-ble-plx": "^0.0.0",
|
||||
"@config-plugins/react-native-callkeep": "^0.0.0",
|
||||
"@config-plugins/react-native-dynamic-app-icon": "^0.0.0",
|
||||
"@config-plugins/react-native-google-cast": "^0.0.0",
|
||||
"@config-plugins/react-native-webrtc": "^0.0.0",
|
||||
"@config-plugins/react-native-quick-actions": "^0.0.0",
|
||||
"expo": "~41.0.0",
|
||||
"expo-localization": "~10.1.0",
|
||||
"expo-splash-screen": "~0.10.2",
|
||||
"expo-status-bar": "~1.0.4",
|
||||
"expo-updates": "~0.5.4",
|
||||
"@config-plugins/detox": "^1.0.0",
|
||||
"expo": "^43.0.0",
|
||||
"expo-localization": "~11.0.0",
|
||||
"expo-splash-screen": "~0.13.5",
|
||||
"expo-status-bar": "~1.1.0",
|
||||
"expo-updates": "~0.10.13",
|
||||
"ffmpeg-kit-react-native": "^4.5.0",
|
||||
"i18n-js": "^3.8.0",
|
||||
"luxon": "^1.27.0",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "~0.63.4",
|
||||
"react-native-ble-plx": "^2.0.2",
|
||||
"react-native-quick-actions": "^0.3.13",
|
||||
"react-native-callkeep": "^4.2.0",
|
||||
"react-native-dynamic-app-icon": "^1.1.0",
|
||||
"react": "17.0.1",
|
||||
"react-native": "0.64.3",
|
||||
"react-native-reanimated": "~2.2.0",
|
||||
"react-native-safe-area-context": "3.3.2",
|
||||
"react-native-screens": "~3.8.0",
|
||||
"react-dom": "17.0.1",
|
||||
"react-native-gesture-handler": "~1.10.2",
|
||||
"react-native-google-cast": "^4.1.2",
|
||||
"react-native-reanimated": "~2.1.0",
|
||||
"react-native-screens": "~3.0.0",
|
||||
"react-native-unimodules": "~0.13.3",
|
||||
"react-native-web": "~0.13.12",
|
||||
"react-native-webrtc": "^1.89.3"
|
||||
"react-native-web": "0.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.9.0",
|
||||
"@expo/xcpretty": "^3.1.4",
|
||||
"@babel/core": "^7.12.9",
|
||||
"@babel/plugin-syntax-jsx": "^7.16.0",
|
||||
"@expo/metro-config": "~0.3.0",
|
||||
"@expo/xcpretty": "^4.1.0",
|
||||
"@types/jest": "^26.0.19",
|
||||
"@types/luxon": "^1.27.1",
|
||||
"@types/react": "~16.9.35",
|
||||
"@types/react-native": "~0.63.2",
|
||||
"@types/react": "~17.0.21",
|
||||
"@types/react-native": "~0.64.12",
|
||||
"babel-jest": "~25.2.6",
|
||||
"detox": "^18.6.2",
|
||||
"detox": "^19.1.0",
|
||||
"jest": "^26.5.0",
|
||||
"jest-circus": "^26.5.2",
|
||||
"ts-jest": "^26.4.4",
|
||||
"typescript": "~4.0.0"
|
||||
"typescript": "~4.3.5"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script wraps the xcodebuild command and exits with non-zero if the build fails.
|
||||
#
|
||||
# This ensures that CI fails on the correct step instead of attempting to run Detox tests without a
|
||||
# build.
|
||||
|
||||
set -eu
|
||||
|
||||
iosName=$1
|
||||
# Debug or Release
|
||||
configuration=$2
|
||||
# YES or NO
|
||||
UseModernBuildSystem=${3:-"NO"}
|
||||
|
||||
xcodebuild \
|
||||
-workspace ios/$iosName.xcworkspace \
|
||||
-scheme $iosName \
|
||||
-configuration "$configuration" \
|
||||
-sdk iphonesimulator \
|
||||
-derivedDataPath "ios/build" \
|
||||
-UseModernBuildSystem="$UseModernBuildSystem" 2>&1 | npx excpretty ./
|
||||
|
||||
if [ "${PIPESTATUS[0]}" -ne "0" ]; then
|
||||
echo 'Build Failed'
|
||||
set +e
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'Build Succeeded'
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
port=${1:-8081}
|
||||
|
||||
PACKAGER_STATUS_URL="http://localhost:${port}/status"
|
||||
# Check if Metro is running
|
||||
STATUS=$(curl --silent $PACKAGER_STATUS_URL)
|
||||
|
||||
if [ "${STATUS}" = "packager-status:running" ]; then
|
||||
echo " ✅ Verified Metro Bundler is running."
|
||||
else
|
||||
echo " ⚠️ Starting Metro Bundler..."
|
||||
# yarn run clear-metro
|
||||
# yarn start
|
||||
|
||||
commandFile=$(dirname "$0")/start.command
|
||||
cat > ${commandFile} << EOF
|
||||
cd "\$(dirname "\$0")/.."
|
||||
# Run 'react-native start --help' to get more parameters
|
||||
yarn start --port ${port}
|
||||
EOF
|
||||
# execute the file in a new command line window
|
||||
chmod 0755 ${commandFile}
|
||||
case "$OSTYPE" in
|
||||
darwin*)
|
||||
open ${commandFile}
|
||||
;;
|
||||
*)
|
||||
# Spawns a new terminal window and detaches it
|
||||
# Assuming the non-standard variable $TERMINAL is set
|
||||
if [ -z "$TERMINAL" ]; then
|
||||
echo "Could not open a new terminal window. Please make sure to export TERMINAL='your-terminal'"
|
||||
else
|
||||
nohup "$TERMINAL" -e "${commandFile}" </dev/null >/dev/null 2>&1 &
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
fi
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Session } from 'ffmpeg-kit-react-native';
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, SafeAreaView, ScrollView, Text, View } from 'react-native';
|
||||
|
||||
import { useMediaInfo, useResolvedValue } from './useMediaInfo';
|
||||
|
||||
export default function App() {
|
||||
const session = useMediaInfo('http://techslides.com/demos/sample-videos/small.mp4');
|
||||
|
||||
if (!session) {
|
||||
return <View style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}><ActivityIndicator /><Text>Loading...</Text></View>
|
||||
}
|
||||
|
||||
return <SessionInfo session={session} />
|
||||
|
||||
}
|
||||
|
||||
function SessionInfo(props: { session: Session }) {
|
||||
const { session } = props;
|
||||
const duration = useResolvedValue(session.getDuration.bind(session));
|
||||
const output = useResolvedValue(session.getOutput.bind(session));
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<ScrollView>
|
||||
<Text>Duration: {duration}</Text>
|
||||
<Text>Output: {output}</Text>
|
||||
</ScrollView>
|
||||
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { FFprobeKit, Session } from "ffmpeg-kit-react-native";
|
||||
import React from "react";
|
||||
|
||||
function useMounted() {
|
||||
const isMounted = React.useRef(true);
|
||||
React.useEffect(() => {
|
||||
isMounted.current = true;
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
return isMounted;
|
||||
}
|
||||
|
||||
export function useResolvedValue<T>(
|
||||
method: () => Promise<T>
|
||||
): [T | null, Error | null] {
|
||||
const [error, setError] = React.useState<Error | null>(null);
|
||||
const [value, setValue] = React.useState<T | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
method()
|
||||
.then((value) => {
|
||||
if (isMounted) {
|
||||
setValue(value);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (isMounted) {
|
||||
setError(error);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return [value, error];
|
||||
}
|
||||
|
||||
export function useMediaInfo(filePathOrUrl: string) {
|
||||
const [results, setResults] = React.useState<Session | undefined>(undefined);
|
||||
const isMounted = useMounted();
|
||||
React.useEffect(() => {
|
||||
FFprobeKit.getMediaInformationAsync(filePathOrUrl, async (session) => {
|
||||
if (isMounted) {
|
||||
setResults(session);
|
||||
}
|
||||
});
|
||||
}, [filePathOrUrl]);
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { SafeAreaView, StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import Pdf from 'react-native-pdf';
|
||||
|
||||
const source = {
|
||||
uri: "https://samples.leanpub.com/thereactnativebook-sample.pdf",
|
||||
cache: true,
|
||||
};
|
||||
export default function App() {
|
||||
const { width, height } = useWindowDimensions()
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pdf
|
||||
source={source}
|
||||
onLoadComplete={(numberOfPages, filePath) => {
|
||||
console.log(`Number of pages: ${numberOfPages}`);
|
||||
}}
|
||||
onPageChanged={(page, numberOfPages) => {
|
||||
console.log(`Current page: ${page}`);
|
||||
}}
|
||||
onError={(error) => {
|
||||
console.log(error);
|
||||
}}
|
||||
onPressLink={(uri) => {
|
||||
console.log(`Link pressed: ${uri}`);
|
||||
}}
|
||||
style={{ flex: 1, width, height }}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
},
|
||||
|
||||
});
|
||||
@@ -6,7 +6,7 @@ Adding this plugin lets you use `Intl` in your Android app, without using Hermes
|
||||
|
||||
## Expo installation
|
||||
|
||||
> Tested against Expo SDK 42
|
||||
> 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/).
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@config-plugins/android-jsc-intl",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"description": "Config plugin for android-jsc-intl package",
|
||||
"main": "build/withAndroidJscIntl.js",
|
||||
"types": "build/withAndroidJscIntl.d.ts",
|
||||
@@ -26,7 +26,7 @@
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^3.0.3"
|
||||
"@expo/config-plugins": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
|
||||
@@ -2,9 +2,20 @@
|
||||
|
||||
Config plugin to auto configure Detox when the native code is generated (`expo prebuild`).
|
||||
|
||||
## Versioning
|
||||
|
||||
Ensure you use versions that work together!
|
||||
|
||||
| `expo` | `detox` | `@config-plugins/detox` |
|
||||
| ------ | ------- | ----------------------- |
|
||||
| 43.0.0 | 19.1.0 | 1.0.0 |
|
||||
| 40.0.0 | 18.6.2 | 0.0.0 |
|
||||
|
||||
Most notably, the minimum required Kotlin version changed from `1.3.50` in SDK 40 to `1.4.21` in SDK 43. Failure to use the correct versioning could result in Android build errors like `Execution failed for task ':react-native-screens:compileDebugKotlin'.` or `Execution failed for task ':expo:compileDebugKotlin'.`.
|
||||
|
||||
### Add the package to your npm dependencies
|
||||
|
||||
> Tested against Expo SDK 41
|
||||
> Tested against Expo SDK 43
|
||||
|
||||
```
|
||||
yarn add detox @config-plugins/detox
|
||||
|
||||
Generated
+2
-2
@@ -19,8 +19,8 @@ const withDetox = (config, { skipProguard, subdomains } = {}) => {
|
||||
// 4.
|
||||
[
|
||||
withKotlinGradle_1.default,
|
||||
// Minimum version of Kotlin required to work with expo packages in SDK 40
|
||||
"1.3.50",
|
||||
// Minimum version of Kotlin required to work with expo packages in SDK 43
|
||||
"1.4.21",
|
||||
],
|
||||
// 5.
|
||||
withDetoxTestClass_1.withDetoxTestClass,
|
||||
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
import { ConfigPlugin } from "@expo/config-plugins";
|
||||
import { MergeResults } from "@expo/config-plugins/build/utils/generateCode";
|
||||
/**
|
||||
* [Step 3](https://github.com/wix/Detox/blob/master/docs/Introduction.Android.md#3-add-the-native-detox-dependency) Add detox to the project build.gradle.
|
||||
* @param config
|
||||
*/
|
||||
declare const withDetoxProjectGradle: ConfigPlugin;
|
||||
export declare function setGradleMaven(buildGradle: string): string;
|
||||
export declare function addDetoxImport(src: string): MergeResults;
|
||||
export default withDetoxProjectGradle;
|
||||
+37
-11
@@ -1,11 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setGradleMaven = void 0;
|
||||
exports.addDetoxImport = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
|
||||
// Because we need the package to be added AFTER the React and Google maven packages, we create a new allprojects.
|
||||
// It's ok to have multiple allprojects.repositories, so we create a new one since it's cheaper than tokenizing
|
||||
// the existing block to find the correct place to insert our Detox maven.
|
||||
const gradleMaven = 'allprojects { repositories { maven { url "$rootDir/../node_modules/detox/Detox-android" } } }';
|
||||
// the existing block to find the correct place to insert our camera maven.
|
||||
const gradleMaven = [
|
||||
`def detoxMavenPath = new File(["node", "--print", "require.resolve('detox/package.json')"].execute(null, rootDir).text.trim(), "../Detox-android")`,
|
||||
`allprojects { repositories { maven { url(detoxMavenPath) } } }`,
|
||||
].join("\n");
|
||||
/**
|
||||
* [Step 3](https://github.com/wix/Detox/blob/master/docs/Introduction.Android.md#3-add-the-native-detox-dependency) Add detox to the project build.gradle.
|
||||
* @param config
|
||||
@@ -13,7 +17,7 @@ const gradleMaven = 'allprojects { repositories { maven { url "$rootDir/../node_
|
||||
const withDetoxProjectGradle = (config) => {
|
||||
return config_plugins_1.withProjectBuildGradle(config, (config) => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setGradleMaven(config.modResults.contents);
|
||||
config.modResults.contents = addDetoxImport(config.modResults.contents).contents;
|
||||
}
|
||||
else {
|
||||
throw new Error("Cannot add Detox maven gradle because the project build.gradle is not groovy");
|
||||
@@ -21,13 +25,35 @@ const withDetoxProjectGradle = (config) => {
|
||||
return config;
|
||||
});
|
||||
};
|
||||
function setGradleMaven(buildGradle) {
|
||||
// If this specific line is present, skip.
|
||||
// This also enables users in bare workflow to comment out the line to prevent detox from adding it back.
|
||||
if (buildGradle.includes("detox/Detox-android")) {
|
||||
return buildGradle;
|
||||
function addDetoxImport(src) {
|
||||
return appendContents({
|
||||
tag: "detox-import",
|
||||
src,
|
||||
newSrc: gradleMaven,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
exports.addDetoxImport = addDetoxImport;
|
||||
// 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 buildGradle + `\n${gradleMaven}\n`;
|
||||
return { contents: src, didClear: false, didMerge: false };
|
||||
}
|
||||
exports.setGradleMaven = setGradleMaven;
|
||||
exports.default = withDetoxProjectGradle;
|
||||
Generated
+1
@@ -12,6 +12,7 @@ const kotlinClassPath = "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersio
|
||||
*/
|
||||
const withKotlinGradle = (config, version) => {
|
||||
return config_plugins_1.withProjectBuildGradle(config, (config) => {
|
||||
console.log(`[config-plugins/detox] Setting Kotlin version to: ${version}. This could lead to Android build issues.`);
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setKotlinVersion(config.modResults.contents, version);
|
||||
config.modResults.contents = setKotlinClassPath(config.modResults.contents);
|
||||
|
||||
+4
-3
@@ -26,9 +26,10 @@ function addDetoxProguardRules(buildGradle) {
|
||||
return buildGradle;
|
||||
}
|
||||
return buildGradle.replace(/proguardFiles getDefaultProguardFile\("proguard-android.txt"\),\s?"proguard-rules.pro"/, `proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
// Detox-specific additions to pro-guard
|
||||
proguardFile "\${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
|
||||
`);
|
||||
// Detox-specific additions to pro-guard
|
||||
def detoxProguardRulesPath = new File(["node", "--print", "require.resolve('detox/package.json')"].execute(null, rootDir).text.trim(), "../android/detox/proguard-rules-app.pro")
|
||||
proguardFile(detoxProguardRulesPath)
|
||||
`);
|
||||
}
|
||||
exports.addDetoxProguardRules = addDetoxProguardRules;
|
||||
exports.default = withProguardGradle;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@config-plugins/detox",
|
||||
"version": "0.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "Config plugin to auto configure detox on prebuild",
|
||||
"main": "build/withDetox.js",
|
||||
"types": "build/withDetox.d.ts",
|
||||
@@ -25,7 +25,7 @@
|
||||
"e2e"
|
||||
],
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^3.0.3"
|
||||
"@expo/config-plugins": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
|
||||
@@ -16,9 +16,10 @@ android {
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile(\\"proguard-android.txt\\"), \\"proguard-rules.pro\\"
|
||||
// Detox-specific additions to pro-guard
|
||||
proguardFile \\"\${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro\\"
|
||||
|
||||
// Detox-specific additions to pro-guard
|
||||
def detoxProguardRulesPath = new File([\\"node\\", \\"--print\\", \\"require.resolve('detox/package.json')\\"].execute(null, rootDir).text.trim(), \\"../android/detox/proguard-rules-app.pro\\")
|
||||
proguardFile(detoxProguardRulesPath)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ describe(addDetoxProguardRules, () => {
|
||||
const applied = addDetoxProguardRules(buildGradleFixture);
|
||||
expect(applied).toMatchSnapshot();
|
||||
expect(applied).toMatch(/detox/);
|
||||
expect(applied).toMatch(/proguardFile "\${rootProject.projectDir}\//);
|
||||
expect(applied).toMatch(/proguardFile\(detoxProguardRulesPath\)/);
|
||||
});
|
||||
it(`doesn't add proguard rules to gradle if proguard is missing from the template`, () => {
|
||||
expect(
|
||||
|
||||
@@ -36,8 +36,8 @@ const withDetox: ConfigPlugin<{
|
||||
// 4.
|
||||
[
|
||||
withKotlinGradle,
|
||||
// Minimum version of Kotlin required to work with expo packages in SDK 40
|
||||
"1.3.50",
|
||||
// Minimum version of Kotlin required to work with expo packages in SDK 43
|
||||
"1.4.21",
|
||||
],
|
||||
// 5.
|
||||
withDetoxTestClass,
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { ConfigPlugin, withProjectBuildGradle } from "@expo/config-plugins";
|
||||
import {
|
||||
createGeneratedHeaderComment,
|
||||
MergeResults,
|
||||
removeGeneratedContents,
|
||||
} from "@expo/config-plugins/build/utils/generateCode";
|
||||
|
||||
// Because we need the package to be added AFTER the React and Google maven packages, we create a new allprojects.
|
||||
// It's ok to have multiple allprojects.repositories, so we create a new one since it's cheaper than tokenizing
|
||||
// the existing block to find the correct place to insert our Detox maven.
|
||||
const gradleMaven =
|
||||
'allprojects { repositories { maven { url "$rootDir/../node_modules/detox/Detox-android" } } }';
|
||||
// the existing block to find the correct place to insert our camera maven.
|
||||
const gradleMaven = [
|
||||
`def detoxMavenPath = new File(["node", "--print", "require.resolve('detox/package.json')"].execute(null, rootDir).text.trim(), "../Detox-android")`,
|
||||
`allprojects { repositories { maven { url(detoxMavenPath) } } }`,
|
||||
].join("\n");
|
||||
|
||||
/**
|
||||
* [Step 3](https://github.com/wix/Detox/blob/master/docs/Introduction.Android.md#3-add-the-native-detox-dependency) Add detox to the project build.gradle.
|
||||
@@ -13,7 +20,9 @@ const gradleMaven =
|
||||
const withDetoxProjectGradle: ConfigPlugin = (config) => {
|
||||
return withProjectBuildGradle(config, (config) => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setGradleMaven(config.modResults.contents);
|
||||
config.modResults.contents = addDetoxImport(
|
||||
config.modResults.contents
|
||||
).contents;
|
||||
} else {
|
||||
throw new Error(
|
||||
"Cannot add Detox maven gradle because the project build.gradle is not groovy"
|
||||
@@ -23,14 +32,47 @@ const withDetoxProjectGradle: ConfigPlugin = (config) => {
|
||||
});
|
||||
};
|
||||
|
||||
export function setGradleMaven(buildGradle: string): string {
|
||||
// If this specific line is present, skip.
|
||||
// This also enables users in bare workflow to comment out the line to prevent detox from adding it back.
|
||||
if (buildGradle.includes("detox/Detox-android")) {
|
||||
return buildGradle;
|
||||
}
|
||||
export function addDetoxImport(src: string): MergeResults {
|
||||
return appendContents({
|
||||
tag: "detox-import",
|
||||
src,
|
||||
newSrc: gradleMaven,
|
||||
comment: "//",
|
||||
});
|
||||
}
|
||||
|
||||
return buildGradle + `\n${gradleMaven}\n`;
|
||||
// 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 default withDetoxProjectGradle;
|
||||
@@ -13,6 +13,9 @@ const kotlinClassPath =
|
||||
*/
|
||||
const withKotlinGradle: ConfigPlugin<string> = (config, version) => {
|
||||
return withProjectBuildGradle(config, (config) => {
|
||||
console.log(
|
||||
`[config-plugins/detox] Setting Kotlin version to: ${version}. This could lead to Android build issues.`
|
||||
);
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setKotlinVersion(
|
||||
config.modResults.contents,
|
||||
|
||||
@@ -31,9 +31,10 @@ export function addDetoxProguardRules(buildGradle: string): string {
|
||||
return buildGradle.replace(
|
||||
/proguardFiles getDefaultProguardFile\("proguard-android.txt"\),\s?"proguard-rules.pro"/,
|
||||
`proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
// Detox-specific additions to pro-guard
|
||||
proguardFile "\${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
|
||||
`
|
||||
// Detox-specific additions to pro-guard
|
||||
def detoxProguardRulesPath = new File(["node", "--print", "require.resolve('detox/package.json')"].execute(null, rootDir).text.trim(), "../android/detox/proguard-rules-app.pro")
|
||||
proguardFile(detoxProguardRulesPath)
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// @generated by expo-module-scripts
|
||||
module.exports = require('expo-module-scripts/eslintrc.base.js');
|
||||
@@ -0,0 +1,60 @@
|
||||
# config-plugins/ffmpeg-kit-react-native
|
||||
|
||||
Config plugin to auto configure ffmpeg-kit-react-native when the native code is generated (`expo prebuild`).
|
||||
|
||||
## Expo installation
|
||||
|
||||
> Tested against Expo SDK 43. Only the `min` package appeared to work on Android. Most packages worked on iOS.
|
||||
|
||||
> 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
|
||||
yarn add ffmpeg-kit-react-native @config-plugins/ffmpeg-kit-react-native
|
||||
```
|
||||
|
||||
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/ffmpeg-kit-react-native"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
- `package` (_string_): Package to use. Options are: min, min-gpl, https, https-gpl, audio, video, full, full-gpl. [Learn more](https://github.com/tanersener/ffmpeg-kit/tree/main/react-native#211-package-names).
|
||||
- `ios.package` (_string_): Specify an iOS only package.
|
||||
- `android.package` (_string_): Specify an Android only package.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"plugins": [
|
||||
[
|
||||
"@config-plugins/ffmpeg-kit-react-native",
|
||||
{
|
||||
"package": "min",
|
||||
"ios": {
|
||||
"package": "video"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- This plugin changes the minimum deployment target on iOS to `12.1` (from `12`) and Android to `24` (from `21`) which may break other packages in your app!
|
||||
- Changing the "package" field on iOS will require re-running `pod install` after updating with `expo prebuild -p ios --no-install`.
|
||||
- Most of the modifications in this plugin are dangerous meaning it's highly likely that something may break over time. The plugin is heavily unit-tested to attempt to prevent breaking though.
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require("./build/withFFMPEG");
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { ConfigPlugin } from "@expo/config-plugins";
|
||||
export declare const withAndroidFFMPEGPackage: ConfigPlugin<string | undefined>;
|
||||
export declare function addPackageName(src: string, packageName?: string): string;
|
||||
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.addPackageName = exports.withAndroidFFMPEGPackage = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
|
||||
const withAndroidFFMPEGPackage = (config, packageName) => {
|
||||
return config_plugins_1.withProjectBuildGradle(config, (config) => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = addPackageName(config.modResults.contents, packageName);
|
||||
}
|
||||
else {
|
||||
throw new Error("Cannot add camera maven gradle because the build.gradle is not groovy");
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withAndroidFFMPEGPackage = withAndroidFFMPEGPackage;
|
||||
function addPackageName(src, packageName) {
|
||||
const tag = "ffmpeg-kit-react-native-package";
|
||||
const gradleMaven = packageName
|
||||
? `ext { ffmpegKitPackage = "${packageName}" }`
|
||||
: "";
|
||||
return appendContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: gradleMaven,
|
||||
comment: "//",
|
||||
}).contents;
|
||||
}
|
||||
exports.addPackageName = addPackageName;
|
||||
// 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.
|
||||
let sanitizedTarget = generateCode_1.removeGeneratedContents(src, tag);
|
||||
if (sanitizedTarget)
|
||||
sanitizedTarget += "\n";
|
||||
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 };
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { ConfigPlugin } from "@expo/config-plugins";
|
||||
/** Sets a numeric version for a value in the project.gradle buildscript.ext object to be at least the provided props.minVersion, if the existing value is greater then no change will be made. */
|
||||
export declare const withBuildScriptExtMinimumVersion: ConfigPlugin<{
|
||||
name: string;
|
||||
minVersion: number;
|
||||
}>;
|
||||
export declare function setMinBuildScriptExtVersion(buildGradle: string, { name, minVersion }: {
|
||||
name: string;
|
||||
minVersion: number;
|
||||
}): string;
|
||||
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
// Copied from https://github.com/expo/expo-cli/blob/main/packages/config-plugins/src/android/Version.ts
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setMinBuildScriptExtVersion = exports.withBuildScriptExtMinimumVersion = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
/** Sets a numeric version for a value in the project.gradle buildscript.ext object to be at least the provided props.minVersion, if the existing value is greater then no change will be made. */
|
||||
const withBuildScriptExtMinimumVersion = (config, props) => {
|
||||
return config_plugins_1.withProjectBuildGradle(config, (config) => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setMinBuildScriptExtVersion(config.modResults.contents, props);
|
||||
}
|
||||
else {
|
||||
config_plugins_1.WarningAggregator.addWarningAndroid("withBuildScriptExtVersion", `Cannot automatically configure project build.gradle if it's not groovy`);
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withBuildScriptExtMinimumVersion = withBuildScriptExtMinimumVersion;
|
||||
function setMinBuildScriptExtVersion(buildGradle, { name, minVersion }) {
|
||||
var _a;
|
||||
const regex = new RegExp(`(${name}\\s?=\\s?)(\\d+(?:\\.\\d+)?)`);
|
||||
const currentVersion = (_a = buildGradle.match(regex)) === null || _a === void 0 ? void 0 : _a[2];
|
||||
if (!currentVersion) {
|
||||
config_plugins_1.WarningAggregator.addWarningAndroid("withBuildScriptExtVersion", `Cannot set minimum buildscript.ext.${name} version because the property "${name}" cannot be found or does not have a numeric value.`);
|
||||
// TODO: Maybe just add the property...
|
||||
return buildGradle;
|
||||
}
|
||||
const currentVersionNum = Number(currentVersion);
|
||||
return buildGradle.replace(regex, `$1${Math.max(minVersion, currentVersionNum)}`);
|
||||
}
|
||||
exports.setMinBuildScriptExtVersion = setMinBuildScriptExtVersion;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { ConfigPlugin } from "@expo/config-plugins";
|
||||
export declare const withPodfilePropertiesPackage: ConfigPlugin<string>;
|
||||
/** Dangerously adds the custom import to the CocoaPods. */
|
||||
export declare const withCocoaPodsImport: ConfigPlugin;
|
||||
export declare function addCocoaPodsImport(src: string): string;
|
||||
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.addCocoaPodsImport = exports.withCocoaPodsImport = exports.withPodfilePropertiesPackage = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const ios_plugins_1 = require("@expo/config-plugins/build/plugins/ios-plugins");
|
||||
const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
|
||||
const fs_1 = require("fs");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const withPodfilePropertiesPackage = (config, packageName) => {
|
||||
return ios_plugins_1.withPodfileProperties(config, (config) => {
|
||||
// @ts-ignore: wrong type
|
||||
config.modResults["ffmpeg-kit-react-native.subspecs"] = [
|
||||
packageName,
|
||||
].filter(Boolean);
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withPodfilePropertiesPackage = withPodfilePropertiesPackage;
|
||||
/** Dangerously adds the custom import to the CocoaPods. */
|
||||
const withCocoaPodsImport = (c) => {
|
||||
return config_plugins_1.withDangerousMod(c, [
|
||||
"ios",
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
async (config) => {
|
||||
const file = path_1.default.join(config.modRequest.platformProjectRoot, "Podfile");
|
||||
const contents = await fs_1.promises.readFile(file, "utf8");
|
||||
await fs_1.promises.writeFile(file, addCocoaPodsImport(contents), "utf-8");
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
};
|
||||
exports.withCocoaPodsImport = withCocoaPodsImport;
|
||||
function addCocoaPodsImport(src) {
|
||||
return generateCode_1.mergeContents({
|
||||
tag: `ffmpeg-kit-react-native-import`,
|
||||
src,
|
||||
newSrc: ` pod 'ffmpeg-kit-react-native', :subspecs => podfile_properties['ffmpeg-kit-react-native.subspecs'] || [], :podspec => File.join(File.dirname(\`node --print "require.resolve('ffmpeg-kit-react-native/package.json')"\`), "ffmpeg-kit-react-native.podspec")`,
|
||||
anchor: /use_native_modules/,
|
||||
// We can't go after the use_native_modules block because it might have parameters, causing it to be multi-line (see react-native template).
|
||||
offset: 0,
|
||||
comment: "#",
|
||||
}).contents;
|
||||
}
|
||||
exports.addCocoaPodsImport = addCocoaPodsImport;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
declare type Package = "min" | "min-gpl" | "https" | "https-gpl" | "audio" | "video" | "full" | "full-gpl";
|
||||
export declare type Props = {
|
||||
package?: Package;
|
||||
ios?: {
|
||||
package?: Package;
|
||||
};
|
||||
android?: {
|
||||
package?: Package;
|
||||
};
|
||||
};
|
||||
export {};
|
||||
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const withAndroidFFMPEGPackage_1 = require("./withAndroidFFMPEGPackage");
|
||||
const withBuildScriptExtMinimumVersion_1 = require("./withBuildScriptExtMinimumVersion");
|
||||
const withCocoaPodsImport_1 = require("./withCocoaPodsImport");
|
||||
const withIosDeploymentTarget_1 = require("./withIosDeploymentTarget");
|
||||
let pkg = {
|
||||
name: "ffmpeg-kit-react-native",
|
||||
};
|
||||
try {
|
||||
pkg = require("ffmpeg-kit-react-native/package.json");
|
||||
}
|
||||
catch (_a) {
|
||||
// empty catch block
|
||||
}
|
||||
const withFFMPEG = (config, _props) => {
|
||||
var _a, _b;
|
||||
const props = _props || {};
|
||||
const iosPackage = ((_a = props.ios) === null || _a === void 0 ? void 0 : _a.package) || props.package;
|
||||
const androidPackage = ((_b = props.android) === null || _b === void 0 ? void 0 : _b.package) || props.package;
|
||||
return config_plugins_1.withPlugins(config, [
|
||||
// iOS
|
||||
[withCocoaPodsImport_1.withPodfilePropertiesPackage, iosPackage],
|
||||
[
|
||||
withIosDeploymentTarget_1.withIosDeploymentTarget,
|
||||
// https://github.com/tanersener/ffmpeg-kit/tree/main/react-native#211-package-names
|
||||
{ deploymentTarget: "12.1" },
|
||||
],
|
||||
withCocoaPodsImport_1.withCocoaPodsImport,
|
||||
// Android
|
||||
// Set min SDK Version to 24.
|
||||
[
|
||||
withBuildScriptExtMinimumVersion_1.withBuildScriptExtMinimumVersion,
|
||||
{
|
||||
name: "minSdkVersion",
|
||||
minVersion: 24,
|
||||
},
|
||||
],
|
||||
[withAndroidFFMPEGPackage_1.withAndroidFFMPEGPackage, androidPackage],
|
||||
]);
|
||||
};
|
||||
module.exports = config_plugins_1.createRunOncePlugin(withFFMPEG, pkg.name, pkg.version);
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { ConfigPlugin, XcodeProject } from "@expo/config-plugins";
|
||||
declare type IosDeploymentTargetConfigPlugin = ConfigPlugin<{
|
||||
deploymentTarget: string;
|
||||
}>;
|
||||
export declare const withIosDeploymentTarget: IosDeploymentTargetConfigPlugin;
|
||||
export declare function updateDeploymentTargetPodfile(contents: string, deploymentTarget: string): string;
|
||||
export declare function updateDeploymentTargetXcodeProject(project: XcodeProject, deploymentTarget: string): XcodeProject;
|
||||
export {};
|
||||
@@ -0,0 +1,60 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.updateDeploymentTargetXcodeProject = exports.updateDeploymentTargetPodfile = exports.withIosDeploymentTarget = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const semver_1 = __importDefault(require("semver"));
|
||||
const withIosDeploymentTarget = (config, props) => {
|
||||
config = withIosDeploymentTargetPodfile(config, props);
|
||||
config = withIosDeploymentTargetXcodeProject(config, props);
|
||||
return config;
|
||||
};
|
||||
exports.withIosDeploymentTarget = withIosDeploymentTarget;
|
||||
const withIosDeploymentTargetPodfile = (config, props) => {
|
||||
return config_plugins_1.withDangerousMod(config, [
|
||||
"ios",
|
||||
async (config) => {
|
||||
const podfile = path_1.default.join(config.modRequest.platformProjectRoot, "Podfile");
|
||||
let contents = await fs_1.default.promises.readFile(podfile, "utf8");
|
||||
contents = updateDeploymentTargetPodfile(contents, props.deploymentTarget);
|
||||
await fs_1.default.promises.writeFile(podfile, contents);
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
};
|
||||
function updateDeploymentTargetPodfile(contents, deploymentTarget) {
|
||||
return contents.replace(/^(\s*platform :ios, ['"])([\d.]+)(['"])/gm, (match, prefix, version, suffix) => {
|
||||
if (semver_1.default.lt(toSemVer(version), toSemVer(deploymentTarget))) {
|
||||
return `${prefix}${deploymentTarget}${suffix}`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
}
|
||||
exports.updateDeploymentTargetPodfile = updateDeploymentTargetPodfile;
|
||||
const withIosDeploymentTargetXcodeProject = (config, props) => {
|
||||
return config_plugins_1.withXcodeProject(config, (config) => {
|
||||
config.modResults = updateDeploymentTargetXcodeProject(config.modResults, props.deploymentTarget);
|
||||
return config;
|
||||
});
|
||||
};
|
||||
function updateDeploymentTargetXcodeProject(project, deploymentTarget) {
|
||||
const configurations = project.pbxXCBuildConfigurationSection();
|
||||
// @ts-ignore
|
||||
for (const { buildSettings } of Object.values(configurations !== null && configurations !== void 0 ? configurations : {})) {
|
||||
const currDeploymentTarget = buildSettings === null || buildSettings === void 0 ? void 0 : buildSettings.IPHONEOS_DEPLOYMENT_TARGET;
|
||||
if (currDeploymentTarget &&
|
||||
semver_1.default.lt(toSemVer(currDeploymentTarget), toSemVer(deploymentTarget))) {
|
||||
buildSettings.IPHONEOS_DEPLOYMENT_TARGET = deploymentTarget;
|
||||
}
|
||||
}
|
||||
return project;
|
||||
}
|
||||
exports.updateDeploymentTargetXcodeProject = updateDeploymentTargetXcodeProject;
|
||||
function toSemVer(version) {
|
||||
var _a;
|
||||
return (_a = semver_1.default.coerce(version)) !== null && _a !== void 0 ? _a : new semver_1.default.SemVer("0.0.0");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('expo-module-scripts/jest-preset-plugin');
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@config-plugins/ffmpeg-kit-react-native",
|
||||
"version": "0.0.0",
|
||||
"description": "Config plugin to auto configure FFMPEG on prebuild",
|
||||
"main": "build/withFFMPEG.js",
|
||||
"types": "build/withFFMPEG.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": [
|
||||
"ffmpeg-kit-react-native",
|
||||
"react-native",
|
||||
"expo"
|
||||
],
|
||||
"jest": {
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^4.0.7",
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/semver": "^7.3.9",
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`addPackageName adds undefined package 1`] = `
|
||||
"
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = \\"29.0.2\\"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 29
|
||||
targetSdkVersion = 29
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath(\\"com.android.tools.build:gradle:3.5.3\\")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
url(\\"$rootDir/../node_modules/react-native/android\\")
|
||||
}
|
||||
maven {
|
||||
url(\\"$rootDir/../node_modules/jsc-android/dist\\")
|
||||
}
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
// @generated begin ffmpeg-kit-react-native-package - expo prebuild (DO NOT MODIFY) sync-da39a3ee5e6b4b0d3255bfef95601890afd80709
|
||||
|
||||
// @generated end ffmpeg-kit-react-native-package"
|
||||
`;
|
||||
|
||||
exports[`addPackageName redefines the package 1`] = `
|
||||
"
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = \\"29.0.2\\"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 29
|
||||
targetSdkVersion = 29
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath(\\"com.android.tools.build:gradle:3.5.3\\")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
url(\\"$rootDir/../node_modules/react-native/android\\")
|
||||
}
|
||||
maven {
|
||||
url(\\"$rootDir/../node_modules/jsc-android/dist\\")
|
||||
}
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
// @generated begin ffmpeg-kit-react-native-package - expo prebuild (DO NOT MODIFY) sync-3d2afee3bf3e62c8e7e2f5faaff1396dc491d7de
|
||||
ext { ffmpegKitPackage = \\"min\\" }
|
||||
// @generated end ffmpeg-kit-react-native-package"
|
||||
`;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`addCocoaPodsImport adds import 1`] = `
|
||||
"
|
||||
platform :ios, '12.1'
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {}
|
||||
|
||||
target 'yolo68' do
|
||||
use_expo_modules!
|
||||
# @generated begin ffmpeg-kit-react-native-import - expo prebuild (DO NOT MODIFY) sync-a79a962553e9fe0961383adf881bd0dada86e29b
|
||||
pod 'ffmpeg-kit-react-native', :subspecs => podfile_properties['ffmpeg-kit-react-native.subspecs'] || [], :podspec => File.join(File.dirname(\`node --print \\"require.resolve('ffmpeg-kit-react-native/package.json')\\"\`), \\"ffmpeg-kit-react-native.podspec\\")
|
||||
# @generated end ffmpeg-kit-react-native-import
|
||||
config = use_native_modules!
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes'
|
||||
)
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
|
||||
installer.pods_project.targets.each do |target|
|
||||
if (target.name&.eql?('FBReactNativeSpec'))
|
||||
target.build_phases.each do |build_phase|
|
||||
if (build_phase.respond_to?(:name) && build_phase.name.eql?('[CP-User] Generate Specs'))
|
||||
target.build_phases.move(build_phase, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,46 @@
|
||||
import { addPackageName } from "../withAndroidFFMPEGPackage";
|
||||
|
||||
const buildGradleFixture = `
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "29.0.2"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 29
|
||||
targetSdkVersion = 29
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:3.5.3")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
url("$rootDir/../node_modules/react-native/android")
|
||||
}
|
||||
maven {
|
||||
url("$rootDir/../node_modules/jsc-android/dist")
|
||||
}
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
`;
|
||||
describe(addPackageName, () => {
|
||||
it(`adds undefined package`, () => {
|
||||
expect(addPackageName(buildGradleFixture, undefined)).toMatchSnapshot();
|
||||
});
|
||||
it(`redefines the package`, () => {
|
||||
let value = addPackageName(buildGradleFixture, "video");
|
||||
value = addPackageName(value, "audio");
|
||||
value = addPackageName(value, undefined);
|
||||
value = addPackageName(value, "min");
|
||||
expect(value).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import { WarningAggregator } from "@expo/config-plugins";
|
||||
import { setMinBuildScriptExtVersion } from "../withBuildScriptExtMinimumVersion";
|
||||
|
||||
const EXAMPLE_PROJECT_BUILD_GRADLE = `
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "29.0.3"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 30
|
||||
targetSdkVersion = 30
|
||||
oddFormat = 30.2
|
||||
}
|
||||
repositories {
|
||||
}
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url(new File(["node", "--print", "require.resolve('jsc-android/package.json')"].execute(null, rootDir).text.trim(), "../dist"))
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
describe(setMinBuildScriptExtVersion, () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore: jest
|
||||
// eslint-disable-next-line import/namespace
|
||||
WarningAggregator.addWarningAndroid = jest.fn();
|
||||
});
|
||||
|
||||
it(`sets the minSdkVersion in build.gradle if minSdkVersion is given`, () => {
|
||||
expect(
|
||||
setMinBuildScriptExtVersion(EXAMPLE_PROJECT_BUILD_GRADLE, {
|
||||
name: "minSdkVersion",
|
||||
minVersion: 22,
|
||||
})
|
||||
).toMatch(/minSdkVersion = 22/);
|
||||
expect(WarningAggregator.addWarningAndroid).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`sets the oddFormat in build.gradle if oddFormat is given`, () => {
|
||||
expect(
|
||||
setMinBuildScriptExtVersion(EXAMPLE_PROJECT_BUILD_GRADLE, {
|
||||
name: "oddFormat",
|
||||
minVersion: 30.3,
|
||||
})
|
||||
).toMatch(/oddFormat = 30\.3/);
|
||||
expect(WarningAggregator.addWarningAndroid).not.toHaveBeenCalled();
|
||||
});
|
||||
it(`does not change the compileSdkVersion in build.gradle if compileSdkVersion is lower than the existing value`, () => {
|
||||
expect(
|
||||
setMinBuildScriptExtVersion(EXAMPLE_PROJECT_BUILD_GRADLE, {
|
||||
name: "compileSdkVersion",
|
||||
minVersion: 12,
|
||||
})
|
||||
).toBe(EXAMPLE_PROJECT_BUILD_GRADLE);
|
||||
expect(WarningAggregator.addWarningAndroid).not.toHaveBeenCalled();
|
||||
});
|
||||
it(`warns when it cannot find the requested value`, () => {
|
||||
expect(
|
||||
setMinBuildScriptExtVersion(EXAMPLE_PROJECT_BUILD_GRADLE, {
|
||||
name: "foobar",
|
||||
minVersion: 12,
|
||||
})
|
||||
).toBe(EXAMPLE_PROJECT_BUILD_GRADLE);
|
||||
expect(WarningAggregator.addWarningAndroid).toBeCalledWith(
|
||||
"withBuildScriptExtVersion",
|
||||
'Cannot set minimum buildscript.ext.foobar version because the property "foobar" cannot be found or does not have a numeric value.'
|
||||
);
|
||||
});
|
||||
it(`does warns when targeting a property with a string value`, () => {
|
||||
expect(
|
||||
setMinBuildScriptExtVersion(EXAMPLE_PROJECT_BUILD_GRADLE, {
|
||||
name: "buildToolsVersion",
|
||||
minVersion: 12,
|
||||
})
|
||||
).toBe(EXAMPLE_PROJECT_BUILD_GRADLE);
|
||||
expect(WarningAggregator.addWarningAndroid).toBeCalledWith(
|
||||
"withBuildScriptExtVersion",
|
||||
'Cannot set minimum buildscript.ext.buildToolsVersion version because the property "buildToolsVersion" cannot be found or does not have a numeric value.'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { addCocoaPodsImport } from "../withCocoaPodsImport";
|
||||
|
||||
const fixture = `
|
||||
platform :ios, '12.1'
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {}
|
||||
|
||||
target 'yolo68' do
|
||||
use_expo_modules!
|
||||
config = use_native_modules!
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes'
|
||||
)
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
|
||||
installer.pods_project.targets.each do |target|
|
||||
if (target.name&.eql?('FBReactNativeSpec'))
|
||||
target.build_phases.each do |build_phase|
|
||||
if (build_phase.respond_to?(:name) && build_phase.name.eql?('[CP-User] Generate Specs'))
|
||||
target.build_phases.move(build_phase, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
`;
|
||||
describe(addCocoaPodsImport, () => {
|
||||
it(`adds import`, () => {
|
||||
expect(addCocoaPodsImport(fixture)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import { updateDeploymentTargetPodfile } from "../withIosDeploymentTarget";
|
||||
|
||||
describe(updateDeploymentTargetPodfile, () => {
|
||||
it("should update deployment target in Podfile", () => {
|
||||
const contents = `\
|
||||
platform :ios, '10.0'
|
||||
target 'HelloWorld' do
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
`;
|
||||
|
||||
const expectContents = `\
|
||||
platform :ios, '12.0'
|
||||
target 'HelloWorld' do
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
`;
|
||||
expect(updateDeploymentTargetPodfile(contents, "12.0")).toEqual(
|
||||
expectContents
|
||||
);
|
||||
});
|
||||
|
||||
it("should support multiple deployment targets in Podfile", () => {
|
||||
const contents = `\
|
||||
target 'HelloWorld' do
|
||||
platform :ios, '10.0'
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
target 'HelloWorld2' do
|
||||
platform :ios, '9.0'
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
`;
|
||||
|
||||
const expectContents = `\
|
||||
target 'HelloWorld' do
|
||||
platform :ios, '12.0'
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
target 'HelloWorld2' do
|
||||
platform :ios, '12.0'
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
`;
|
||||
expect(updateDeploymentTargetPodfile(contents, "12.0")).toEqual(
|
||||
expectContents
|
||||
);
|
||||
});
|
||||
|
||||
it("should leave unmodified if deployment target meets requirements", () => {
|
||||
const contents = `\
|
||||
platform :ios, '12.0'
|
||||
target 'HelloWorld' do
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath]
|
||||
)
|
||||
end
|
||||
`;
|
||||
|
||||
expect(updateDeploymentTargetPodfile(contents, "12.0")).toEqual(contents);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import { ConfigPlugin, withProjectBuildGradle } from "@expo/config-plugins";
|
||||
import {
|
||||
createGeneratedHeaderComment,
|
||||
MergeResults,
|
||||
removeGeneratedContents,
|
||||
} from "@expo/config-plugins/build/utils/generateCode";
|
||||
|
||||
export const withAndroidFFMPEGPackage: ConfigPlugin<string | undefined> = (
|
||||
config,
|
||||
packageName
|
||||
) => {
|
||||
return withProjectBuildGradle(config, (config) => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = addPackageName(
|
||||
config.modResults.contents,
|
||||
packageName
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Cannot add camera maven gradle because the build.gradle is not groovy"
|
||||
);
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
|
||||
export function addPackageName(src: string, packageName?: string): string {
|
||||
const tag = "ffmpeg-kit-react-native-package";
|
||||
const gradleMaven = packageName
|
||||
? `ext { ffmpegKitPackage = "${packageName}" }`
|
||||
: "";
|
||||
return appendContents({
|
||||
tag,
|
||||
src,
|
||||
newSrc: gradleMaven,
|
||||
comment: "//",
|
||||
}).contents;
|
||||
}
|
||||
|
||||
// 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.
|
||||
let sanitizedTarget = removeGeneratedContents(src, tag);
|
||||
if (sanitizedTarget) sanitizedTarget += "\n";
|
||||
|
||||
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 };
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copied from https://github.com/expo/expo-cli/blob/main/packages/config-plugins/src/android/Version.ts
|
||||
|
||||
import {
|
||||
ConfigPlugin,
|
||||
WarningAggregator,
|
||||
withProjectBuildGradle,
|
||||
} from "@expo/config-plugins";
|
||||
|
||||
/** Sets a numeric version for a value in the project.gradle buildscript.ext object to be at least the provided props.minVersion, if the existing value is greater then no change will be made. */
|
||||
export const withBuildScriptExtMinimumVersion: ConfigPlugin<{
|
||||
name: string;
|
||||
minVersion: number;
|
||||
}> = (config, props) => {
|
||||
return withProjectBuildGradle(config, (config) => {
|
||||
if (config.modResults.language === "groovy") {
|
||||
config.modResults.contents = setMinBuildScriptExtVersion(
|
||||
config.modResults.contents,
|
||||
props
|
||||
);
|
||||
} else {
|
||||
WarningAggregator.addWarningAndroid(
|
||||
"withBuildScriptExtVersion",
|
||||
`Cannot automatically configure project build.gradle if it's not groovy`
|
||||
);
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
|
||||
export function setMinBuildScriptExtVersion(
|
||||
buildGradle: string,
|
||||
{ name, minVersion }: { name: string; minVersion: number }
|
||||
) {
|
||||
const regex = new RegExp(`(${name}\\s?=\\s?)(\\d+(?:\\.\\d+)?)`);
|
||||
const currentVersion = buildGradle.match(regex)?.[2];
|
||||
if (!currentVersion) {
|
||||
WarningAggregator.addWarningAndroid(
|
||||
"withBuildScriptExtVersion",
|
||||
`Cannot set minimum buildscript.ext.${name} version because the property "${name}" cannot be found or does not have a numeric value.`
|
||||
);
|
||||
// TODO: Maybe just add the property...
|
||||
return buildGradle;
|
||||
}
|
||||
|
||||
const currentVersionNum = Number(currentVersion);
|
||||
return buildGradle.replace(
|
||||
regex,
|
||||
`$1${Math.max(minVersion, currentVersionNum)}`
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ConfigPlugin, withDangerousMod } from "@expo/config-plugins";
|
||||
import { withPodfileProperties } from "@expo/config-plugins/build/plugins/ios-plugins";
|
||||
import { mergeContents } from "@expo/config-plugins/build/utils/generateCode";
|
||||
import { promises } from "fs";
|
||||
import path from "path";
|
||||
|
||||
export const withPodfilePropertiesPackage: ConfigPlugin<string> = (
|
||||
config,
|
||||
packageName
|
||||
) => {
|
||||
return withPodfileProperties(config, (config) => {
|
||||
// @ts-ignore: wrong type
|
||||
config.modResults["ffmpeg-kit-react-native.subspecs"] = [
|
||||
packageName,
|
||||
].filter(Boolean);
|
||||
return config;
|
||||
});
|
||||
};
|
||||
|
||||
/** Dangerously adds the custom import to the CocoaPods. */
|
||||
export const withCocoaPodsImport: ConfigPlugin = (c) => {
|
||||
return withDangerousMod(c, [
|
||||
"ios",
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
async (config) => {
|
||||
const file = path.join(config.modRequest.platformProjectRoot, "Podfile");
|
||||
|
||||
const contents = await promises.readFile(file, "utf8");
|
||||
|
||||
await promises.writeFile(file, addCocoaPodsImport(contents), "utf-8");
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export function addCocoaPodsImport(src: string): string {
|
||||
return mergeContents({
|
||||
tag: `ffmpeg-kit-react-native-import`,
|
||||
src,
|
||||
newSrc: ` pod 'ffmpeg-kit-react-native', :subspecs => podfile_properties['ffmpeg-kit-react-native.subspecs'] || [], :podspec => File.join(File.dirname(\`node --print "require.resolve('ffmpeg-kit-react-native/package.json')"\`), "ffmpeg-kit-react-native.podspec")`,
|
||||
anchor: /use_native_modules/,
|
||||
// We can't go after the use_native_modules block because it might have parameters, causing it to be multi-line (see react-native template).
|
||||
offset: 0,
|
||||
comment: "#",
|
||||
}).contents;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
ConfigPlugin,
|
||||
createRunOncePlugin,
|
||||
withPlugins,
|
||||
} from "@expo/config-plugins";
|
||||
|
||||
import { withAndroidFFMPEGPackage } from "./withAndroidFFMPEGPackage";
|
||||
import { withBuildScriptExtMinimumVersion } from "./withBuildScriptExtMinimumVersion";
|
||||
import {
|
||||
withCocoaPodsImport,
|
||||
withPodfilePropertiesPackage,
|
||||
} from "./withCocoaPodsImport";
|
||||
import { withIosDeploymentTarget } from "./withIosDeploymentTarget";
|
||||
|
||||
let pkg: { name: string; version?: string } = {
|
||||
name: "ffmpeg-kit-react-native",
|
||||
};
|
||||
try {
|
||||
pkg = require("ffmpeg-kit-react-native/package.json");
|
||||
} catch {
|
||||
// empty catch block
|
||||
}
|
||||
|
||||
type Package =
|
||||
| "min"
|
||||
| "min-gpl"
|
||||
| "https"
|
||||
| "https-gpl"
|
||||
| "audio"
|
||||
| "video"
|
||||
| "full"
|
||||
| "full-gpl";
|
||||
|
||||
export type Props = {
|
||||
package?: Package;
|
||||
ios?: { package?: Package };
|
||||
android?: { package?: Package };
|
||||
};
|
||||
|
||||
const withFFMPEG: ConfigPlugin<void | Props> = (config, _props) => {
|
||||
const props = _props || {};
|
||||
const iosPackage = props.ios?.package || props.package;
|
||||
const androidPackage = props.android?.package || props.package;
|
||||
return withPlugins(config, [
|
||||
// iOS
|
||||
|
||||
[withPodfilePropertiesPackage, iosPackage],
|
||||
[
|
||||
withIosDeploymentTarget,
|
||||
// https://github.com/tanersener/ffmpeg-kit/tree/main/react-native#211-package-names
|
||||
{ deploymentTarget: "12.1" },
|
||||
],
|
||||
withCocoaPodsImport,
|
||||
|
||||
// Android
|
||||
|
||||
// Set min SDK Version to 24.
|
||||
[
|
||||
withBuildScriptExtMinimumVersion,
|
||||
{
|
||||
name: "minSdkVersion",
|
||||
minVersion: 24,
|
||||
},
|
||||
],
|
||||
[withAndroidFFMPEGPackage, androidPackage],
|
||||
]);
|
||||
};
|
||||
|
||||
module.exports = createRunOncePlugin(withFFMPEG, pkg.name, pkg.version);
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copied from https://github.com/expo/expo-cli/blob/main/packages/install-expo-modules/src/plugins/ios/withIosDeploymentTarget.ts
|
||||
import {
|
||||
ConfigPlugin,
|
||||
withDangerousMod,
|
||||
withXcodeProject,
|
||||
XcodeProject,
|
||||
} from "@expo/config-plugins";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import semver from "semver";
|
||||
|
||||
type IosDeploymentTargetConfigPlugin = ConfigPlugin<{
|
||||
deploymentTarget: string;
|
||||
}>;
|
||||
|
||||
export const withIosDeploymentTarget: IosDeploymentTargetConfigPlugin = (
|
||||
config,
|
||||
props
|
||||
) => {
|
||||
config = withIosDeploymentTargetPodfile(config, props);
|
||||
config = withIosDeploymentTargetXcodeProject(config, props);
|
||||
return config;
|
||||
};
|
||||
|
||||
const withIosDeploymentTargetPodfile: IosDeploymentTargetConfigPlugin = (
|
||||
config,
|
||||
props
|
||||
) => {
|
||||
return withDangerousMod(config, [
|
||||
"ios",
|
||||
async (config) => {
|
||||
const podfile = path.join(
|
||||
config.modRequest.platformProjectRoot,
|
||||
"Podfile"
|
||||
);
|
||||
let contents = await fs.promises.readFile(podfile, "utf8");
|
||||
contents = updateDeploymentTargetPodfile(
|
||||
contents,
|
||||
props.deploymentTarget
|
||||
);
|
||||
|
||||
await fs.promises.writeFile(podfile, contents);
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export function updateDeploymentTargetPodfile(
|
||||
contents: string,
|
||||
deploymentTarget: string
|
||||
): string {
|
||||
return contents.replace(
|
||||
/^(\s*platform :ios, ['"])([\d.]+)(['"])/gm,
|
||||
(match, prefix, version, suffix) => {
|
||||
if (semver.lt(toSemVer(version), toSemVer(deploymentTarget))) {
|
||||
return `${prefix}${deploymentTarget}${suffix}`;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const withIosDeploymentTargetXcodeProject: IosDeploymentTargetConfigPlugin = (
|
||||
config,
|
||||
props
|
||||
) => {
|
||||
return withXcodeProject(config, (config) => {
|
||||
config.modResults = updateDeploymentTargetXcodeProject(
|
||||
config.modResults,
|
||||
props.deploymentTarget
|
||||
);
|
||||
return config;
|
||||
});
|
||||
};
|
||||
|
||||
export function updateDeploymentTargetXcodeProject(
|
||||
project: XcodeProject,
|
||||
deploymentTarget: string
|
||||
): XcodeProject {
|
||||
const configurations = project.pbxXCBuildConfigurationSection();
|
||||
// @ts-ignore
|
||||
for (const { buildSettings } of Object.values(configurations ?? {})) {
|
||||
const currDeploymentTarget = buildSettings?.IPHONEOS_DEPLOYMENT_TARGET;
|
||||
if (
|
||||
currDeploymentTarget &&
|
||||
semver.lt(toSemVer(currDeploymentTarget), toSemVer(deploymentTarget))
|
||||
) {
|
||||
buildSettings.IPHONEOS_DEPLOYMENT_TARGET = deploymentTarget;
|
||||
}
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
function toSemVer(version: string): semver.SemVer {
|
||||
return semver.coerce(version) ?? new semver.SemVer("0.0.0");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "expo-module-scripts/tsconfig.plugin",
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
|
||||
}
|
||||
@@ -4,7 +4,7 @@ Config plugin to auto configure iOS iMessage stickers
|
||||
|
||||
## Install
|
||||
|
||||
> Tested against Expo SDK 41
|
||||
> Tested against Expo SDK 43
|
||||
|
||||
```
|
||||
yarn add @config-plugins/ios-stickers
|
||||
|
||||
+6
-2
@@ -44,7 +44,7 @@ function createSticker({ stickerpackPath, name, imageBuffer, extension, accessib
|
||||
name: stickerName,
|
||||
};
|
||||
}
|
||||
exports.withStickerAssets = (config, { stickers, icon, size }) => {
|
||||
const withStickerAssets = (config, { stickers, icon, size }) => {
|
||||
// Default to using the app icon
|
||||
if (!icon) {
|
||||
icon = (config.ios || {}).icon || config.icon;
|
||||
@@ -56,7 +56,9 @@ exports.withStickerAssets = (config, { stickers, icon, size }) => {
|
||||
const stickerRootPath = path_1.default.join(config.modRequest.platformProjectRoot, stickerPackName);
|
||||
const stickersAssetsPath = path_1.default.join(stickerRootPath, STICKERS_ROOT_PATH);
|
||||
// Delete all assets...
|
||||
await fs_1.default.promises.rmdir(stickersAssetsPath, { recursive: true });
|
||||
await fs_1.default.promises
|
||||
.rmdir(stickersAssetsPath, { recursive: true })
|
||||
.catch(() => null);
|
||||
const stickersRootContentsJsonPath = path_1.default.join(stickersAssetsPath, "Contents.json");
|
||||
fs_1.default.mkdirSync(stickersAssetsPath, {
|
||||
recursive: true,
|
||||
@@ -93,6 +95,7 @@ exports.withStickerAssets = (config, { stickers, icon, size }) => {
|
||||
projectRoot: config.modRequest.projectRoot,
|
||||
cacheType: "ios-stickers",
|
||||
}, {
|
||||
resizeMode: "contain",
|
||||
src,
|
||||
});
|
||||
// let results;
|
||||
@@ -118,3 +121,4 @@ exports.withStickerAssets = (config, { stickers, icon, size }) => {
|
||||
},
|
||||
]);
|
||||
};
|
||||
exports.withStickerAssets = withStickerAssets;
|
||||
+2
-1
@@ -28,7 +28,7 @@ const plist_1 = __importDefault(require("@expo/plist"));
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const withStickerXcodeTarget_1 = require("./withStickerXcodeTarget");
|
||||
exports.withStickersPlist = (config, { name }) => {
|
||||
const withStickersPlist = (config, { name }) => {
|
||||
return config_plugins_1.withDangerousMod(config, [
|
||||
"ios",
|
||||
async (config) => {
|
||||
@@ -58,3 +58,4 @@ exports.withStickersPlist = (config, { name }) => {
|
||||
},
|
||||
]);
|
||||
};
|
||||
exports.withStickersPlist = withStickersPlist;
|
||||
+2
-1
@@ -8,7 +8,7 @@ function getProjectStickersName(name) {
|
||||
return `${name} Stickers`;
|
||||
}
|
||||
exports.getProjectStickersName = getProjectStickersName;
|
||||
exports.withStickerXcodeTarget = (config) => {
|
||||
const withStickerXcodeTarget = (config) => {
|
||||
return config_plugins_1.withXcodeProject(config, (config) => {
|
||||
const stickerPackName = getProjectStickersName(config.modRequest.projectName);
|
||||
xcodeSticker_1.addStickersTarget(config.modResults, stickerPackName, config.ios.bundleIdentifier, stickerPackName);
|
||||
@@ -22,3 +22,4 @@ exports.withStickerXcodeTarget = (config) => {
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withStickerXcodeTarget = withStickerXcodeTarget;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@config-plugins/ios-stickers",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"description": "Config plugin to auto configure iOS iMessage stickers",
|
||||
"main": "build/withStickerPack.js",
|
||||
"types": "build/withStickerPack.d.ts",
|
||||
@@ -22,8 +22,8 @@
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^3.0.3",
|
||||
"@expo/image-utils": "^0.3.14",
|
||||
"@expo/config-plugins": "^4.0.7",
|
||||
"@expo/image-utils": "^0.3.17",
|
||||
"xcode": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -93,7 +93,9 @@ export const withStickerAssets: ConfigPlugin<{
|
||||
const stickersAssetsPath = path.join(stickerRootPath, STICKERS_ROOT_PATH);
|
||||
|
||||
// Delete all assets...
|
||||
await fs.promises.rmdir(stickersAssetsPath, { recursive: true });
|
||||
await fs.promises
|
||||
.rmdir(stickersAssetsPath, { recursive: true })
|
||||
.catch(() => null);
|
||||
|
||||
const stickersRootContentsJsonPath = path.join(
|
||||
stickersAssetsPath,
|
||||
@@ -171,6 +173,7 @@ export const withStickerAssets: ConfigPlugin<{
|
||||
cacheType: "ios-stickers",
|
||||
},
|
||||
{
|
||||
resizeMode: "contain",
|
||||
src,
|
||||
} as any
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ Config plugin to auto configure `react-native-ble-plx` when the native code is g
|
||||
|
||||
## Expo installation
|
||||
|
||||
> Tested against Expo SDK 41
|
||||
> 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).
|
||||
@@ -32,6 +32,7 @@ The plugin provides props for extra customization. Every time you change the pro
|
||||
- `isBackgroundEnabled` (_boolean_): Enable background BLE support on Android. Adds `<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>` to the `AndroidManifest.xml`. Default `false`.
|
||||
- `modes` (_string[]_): Adds iOS `UIBackgroundModes` to the `Info.plist`. Options are: `peripheral`, and `central`. Defaults to undefined.
|
||||
- `bluetoothAlwaysPermission` (_string | false_): Sets the iOS `NSBluetoothAlwaysUsageDescription` permission message to the `Info.plist`. Setting `false` will skip adding the permission. Defaults to `Allow $(PRODUCT_NAME) to connect to bluetooth devices`.
|
||||
- `bluetoothPeripheralPermission` (_string | false_): Sets the iOS `NSBluetoothPeripheralUsageDescription` permission message to the `Info.plist`. Setting `false` will skip adding the permission. Defaults to `Allow $(PRODUCT_NAME) to connect to bluetooth devices`.
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -44,7 +45,8 @@ The plugin provides props for extra customization. Every time you change the pro
|
||||
{
|
||||
"isBackgroundEnabled": true,
|
||||
"modes": ["peripheral", "central"],
|
||||
"bluetoothAlwaysPermission": "Allow $(PRODUCT_NAME) to connect to bluetooth devices"
|
||||
"bluetoothAlwaysPermission": "Allow $(PRODUCT_NAME) to connect to bluetooth devices",
|
||||
"bluetoothPeripheralPermission": "Allow $(PRODUCT_NAME) to connect to bluetooth devices"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
+1
@@ -5,5 +5,6 @@ declare const _default: ConfigPlugin<void | {
|
||||
isBackgroundEnabled?: boolean | undefined;
|
||||
modes?: BackgroundMode[] | undefined;
|
||||
bluetoothAlwaysPermission?: string | false | undefined;
|
||||
bluetoothPeripheralPermission?: string | false | undefined;
|
||||
}>;
|
||||
export default _default;
|
||||
+3
-3
@@ -6,17 +6,17 @@ const config_plugins_1 = require("@expo/config-plugins");
|
||||
const withBLEAndroidManifest_1 = require("./withBLEAndroidManifest");
|
||||
const withBLEBackgroundModes_1 = require("./withBLEBackgroundModes");
|
||||
Object.defineProperty(exports, "BackgroundMode", { enumerable: true, get: function () { return withBLEBackgroundModes_1.BackgroundMode; } });
|
||||
const withBluetoothAlwaysPermission_1 = require("./withBluetoothAlwaysPermission");
|
||||
const withBluetoothPermissions_1 = require("./withBluetoothPermissions");
|
||||
const pkg = { name: "react-native-ble-plx", version: "UNVERSIONED" }; //require('react-native-ble-plx/package.json')
|
||||
/**
|
||||
* Apply BLE configuration for Expo SDK 41 projects.
|
||||
* Apply BLE configuration for Expo SDK 42 projects.
|
||||
*/
|
||||
const withBLE = (config, props = {}) => {
|
||||
var _a;
|
||||
const _props = props || {};
|
||||
const isBackgroundEnabled = (_a = _props.isBackgroundEnabled) !== null && _a !== void 0 ? _a : false;
|
||||
// iOS
|
||||
config = withBluetoothAlwaysPermission_1.withBluetoothAlwaysPermission(config, _props);
|
||||
config = withBluetoothPermissions_1.withBluetoothPermissions(config, _props);
|
||||
config = withBLEBackgroundModes_1.withBLEBackgroundModes(config, _props.modes || []);
|
||||
// Android
|
||||
config = config_plugins_1.AndroidConfig.Permissions.withPermissions(config, [
|
||||
|
||||
+2
-1
@@ -3,7 +3,7 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.addBLEHardwareFeatureToManifest = exports.addFineControlPermissionToManifest = exports.withBLEAndroidManifest = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
exports.withBLEAndroidManifest = (config, { isBackgroundEnabled }) => {
|
||||
const withBLEAndroidManifest = (config, { isBackgroundEnabled }) => {
|
||||
return config_plugins_1.withAndroidManifest(config, (config) => {
|
||||
config.modResults = addFineControlPermissionToManifest(config.modResults);
|
||||
if (isBackgroundEnabled) {
|
||||
@@ -12,6 +12,7 @@ exports.withBLEAndroidManifest = (config, { isBackgroundEnabled }) => {
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withBLEAndroidManifest = withBLEAndroidManifest;
|
||||
function addFineControlPermissionToManifest(androidManifest) {
|
||||
var _a;
|
||||
// Add `<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>` to the AndroidManifest.xml
|
||||
|
||||
+2
-1
@@ -19,7 +19,7 @@ const peripheralKey = 'bluetooth-peripheral';
|
||||
/**
|
||||
* Append `UIBackgroundModes` to the `Info.plist`.
|
||||
*/
|
||||
exports.withBLEBackgroundModes = (c, modes) => {
|
||||
const withBLEBackgroundModes = (c, modes) => {
|
||||
return config_plugins_1.withInfoPlist(c, config => {
|
||||
if (!Array.isArray(config.modResults.UIBackgroundModes)) {
|
||||
config.modResults.UIBackgroundModes = [];
|
||||
@@ -37,3 +37,4 @@ exports.withBLEBackgroundModes = (c, modes) => {
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withBLEBackgroundModes = withBLEBackgroundModes;
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
import { ConfigPlugin } from '@expo/config-plugins';
|
||||
export declare const withBluetoothAlwaysPermission: ConfigPlugin<{
|
||||
bluetoothAlwaysPermission?: string | false;
|
||||
}>;
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
/* eslint-disable flowtype/no-types-missing-file-annotation */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.withBluetoothAlwaysPermission = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const BLUETOOTH_ALWAYS = 'Allow $(PRODUCT_NAME) to connect to bluetooth devices';
|
||||
exports.withBluetoothAlwaysPermission = (c, { bluetoothAlwaysPermission } = {}) => {
|
||||
if (bluetoothAlwaysPermission === false) {
|
||||
return c;
|
||||
}
|
||||
return config_plugins_1.withInfoPlist(c, config => {
|
||||
config.modResults.NSBluetoothAlwaysUsageDescription =
|
||||
bluetoothAlwaysPermission || config.modResults.NSBluetoothAlwaysUsageDescription || BLUETOOTH_ALWAYS;
|
||||
return config;
|
||||
});
|
||||
};
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { ConfigPlugin } from "@expo/config-plugins";
|
||||
export declare const withBluetoothPermissions: ConfigPlugin<{
|
||||
bluetoothAlwaysPermission?: string | false;
|
||||
bluetoothPeripheralPermission?: string | false;
|
||||
}>;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.withBluetoothPermissions = void 0;
|
||||
/* eslint-disable flowtype/no-types-missing-file-annotation */
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
const BLUETOOTH_ALWAYS = "Allow $(PRODUCT_NAME) to connect to bluetooth devices";
|
||||
const BLUETOOTH_PERIPHERAL_USAGE = "Allow $(PRODUCT_NAME) to connect to bluetooth devices";
|
||||
const withBluetoothPermissions = (c, { bluetoothAlwaysPermission, bluetoothPeripheralPermission } = {}) => {
|
||||
return config_plugins_1.withInfoPlist(c, (config) => {
|
||||
if (bluetoothAlwaysPermission !== false) {
|
||||
config.modResults.NSBluetoothAlwaysUsageDescription =
|
||||
bluetoothAlwaysPermission ||
|
||||
config.modResults.NSBluetoothAlwaysUsageDescription ||
|
||||
BLUETOOTH_ALWAYS;
|
||||
}
|
||||
if (bluetoothPeripheralPermission !== false) {
|
||||
config.modResults.NSBluetoothPeripheralUsageDescription =
|
||||
bluetoothPeripheralPermission ||
|
||||
config.modResults.NSBluetoothPeripheralUsageDescription ||
|
||||
BLUETOOTH_PERIPHERAL_USAGE;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withBluetoothPermissions = withBluetoothPermissions;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@config-plugins/react-native-ble-plx",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.2",
|
||||
"description": "Config plugin to auto configure react-native-ble-plx on prebuild",
|
||||
"main": "build/withBLE.js",
|
||||
"types": "build/withBLE.d.ts",
|
||||
@@ -26,7 +26,7 @@
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^3.0.3"
|
||||
"@expo/config-plugins": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
|
||||
@@ -11,23 +11,24 @@ import {
|
||||
BackgroundMode,
|
||||
withBLEBackgroundModes,
|
||||
} from "./withBLEBackgroundModes";
|
||||
import { withBluetoothAlwaysPermission } from "./withBluetoothAlwaysPermission";
|
||||
import { withBluetoothPermissions } from "./withBluetoothPermissions";
|
||||
|
||||
const pkg = { name: "react-native-ble-plx", version: "UNVERSIONED" }; //require('react-native-ble-plx/package.json')
|
||||
|
||||
/**
|
||||
* Apply BLE configuration for Expo SDK 41 projects.
|
||||
* Apply BLE configuration for Expo SDK 42 projects.
|
||||
*/
|
||||
const withBLE: ConfigPlugin<{
|
||||
isBackgroundEnabled?: boolean;
|
||||
modes?: BackgroundMode[];
|
||||
bluetoothAlwaysPermission?: string | false;
|
||||
bluetoothPeripheralPermission?: string | false;
|
||||
} | void> = (config, props = {}) => {
|
||||
const _props = props || {};
|
||||
const isBackgroundEnabled = _props.isBackgroundEnabled ?? false;
|
||||
|
||||
// iOS
|
||||
config = withBluetoothAlwaysPermission(config, _props);
|
||||
config = withBluetoothPermissions(config, _props);
|
||||
config = withBLEBackgroundModes(config, _props.modes || []);
|
||||
|
||||
// Android
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/* eslint-disable flowtype/no-types-missing-file-annotation */
|
||||
|
||||
import { ConfigPlugin, withInfoPlist } from '@expo/config-plugins'
|
||||
|
||||
const BLUETOOTH_ALWAYS = 'Allow $(PRODUCT_NAME) to connect to bluetooth devices'
|
||||
|
||||
export const withBluetoothAlwaysPermission: ConfigPlugin<{
|
||||
bluetoothAlwaysPermission?: string | false
|
||||
}> = (c, { bluetoothAlwaysPermission } = {}) => {
|
||||
if (bluetoothAlwaysPermission === false) {
|
||||
return c
|
||||
}
|
||||
return withInfoPlist(c, config => {
|
||||
config.modResults.NSBluetoothAlwaysUsageDescription =
|
||||
bluetoothAlwaysPermission || config.modResults.NSBluetoothAlwaysUsageDescription || BLUETOOTH_ALWAYS
|
||||
return config
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable flowtype/no-types-missing-file-annotation */
|
||||
import { ConfigPlugin, withInfoPlist } from "@expo/config-plugins";
|
||||
|
||||
const BLUETOOTH_ALWAYS =
|
||||
"Allow $(PRODUCT_NAME) to connect to bluetooth devices";
|
||||
const BLUETOOTH_PERIPHERAL_USAGE =
|
||||
"Allow $(PRODUCT_NAME) to connect to bluetooth devices";
|
||||
|
||||
export const withBluetoothPermissions: ConfigPlugin<{
|
||||
bluetoothAlwaysPermission?: string | false;
|
||||
bluetoothPeripheralPermission?: string | false;
|
||||
}> = (c, { bluetoothAlwaysPermission, bluetoothPeripheralPermission } = {}) => {
|
||||
return withInfoPlist(c, (config) => {
|
||||
if (bluetoothAlwaysPermission !== false) {
|
||||
config.modResults.NSBluetoothAlwaysUsageDescription =
|
||||
bluetoothAlwaysPermission ||
|
||||
config.modResults.NSBluetoothAlwaysUsageDescription ||
|
||||
BLUETOOTH_ALWAYS;
|
||||
}
|
||||
if (bluetoothPeripheralPermission !== false) {
|
||||
config.modResults.NSBluetoothPeripheralUsageDescription =
|
||||
bluetoothPeripheralPermission ||
|
||||
config.modResults.NSBluetoothPeripheralUsageDescription ||
|
||||
BLUETOOTH_PERIPHERAL_USAGE;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
// @generated by expo-module-scripts
|
||||
module.exports = require('expo-module-scripts/eslintrc.base.js');
|
||||
@@ -0,0 +1,15 @@
|
||||
# config-plugins/react-native-blob-util
|
||||
|
||||
Config plugin to auto configure [`react-native-blob-util`][lib] when the native code is generated (`expo prebuild`).
|
||||
|
||||
### Add the package to your npm dependencies
|
||||
|
||||
> Tested against Expo SDK 43
|
||||
|
||||
```
|
||||
yarn add react-native-blob-util @config-plugins/react-native-blob-util
|
||||
```
|
||||
|
||||
This plugin is basically Android-only, and required for `react-native-pdf`.
|
||||
|
||||
[lib]: https://www.npmjs.com/package/react-native-blob-util
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require("./build/withReactNativeBlobUtil");
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { AndroidConfig, ConfigPlugin } from "@expo/config-plugins";
|
||||
export declare function appendDownloadCompleteAction(androidManifest: AndroidConfig.Manifest.AndroidManifest): AndroidConfig.Manifest.AndroidManifest;
|
||||
declare const _default: ConfigPlugin<void>;
|
||||
export default _default;
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.appendDownloadCompleteAction = void 0;
|
||||
const config_plugins_1 = require("@expo/config-plugins");
|
||||
let pkg = {
|
||||
name: "react-native-blob-util",
|
||||
};
|
||||
try {
|
||||
pkg = require("react-native-blob-util/package.json");
|
||||
}
|
||||
catch (_a) {
|
||||
// empty catch block
|
||||
}
|
||||
function appendDownloadCompleteAction(androidManifest) {
|
||||
var _a, _b;
|
||||
if (!Array.isArray(androidManifest.manifest.application)) {
|
||||
return androidManifest;
|
||||
}
|
||||
for (const application of androidManifest.manifest.application) {
|
||||
for (const activity of application.activity || []) {
|
||||
if (((_a = activity === null || activity === void 0 ? void 0 : activity.$) === null || _a === void 0 ? void 0 : _a["android:launchMode"]) === "singleTask") {
|
||||
for (const intentFilter of activity["intent-filter"] || []) {
|
||||
const isLauncher = (_b = intentFilter.category) === null || _b === void 0 ? void 0 : _b.some((action) => action.$["android:name"] === "android.intent.category.LAUNCHER");
|
||||
if (!isLauncher)
|
||||
continue;
|
||||
intentFilter.action = intentFilter.action || [];
|
||||
const hasDownloadCompleteAction = intentFilter.action.some((action) => action.$["android:name"] ===
|
||||
"android.intent.action.DOWNLOAD_COMPLETE");
|
||||
if (!hasDownloadCompleteAction) {
|
||||
intentFilter.action.push({
|
||||
$: {
|
||||
"android:name": "android.intent.action.DOWNLOAD_COMPLETE",
|
||||
},
|
||||
});
|
||||
return androidManifest;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return androidManifest;
|
||||
}
|
||||
exports.appendDownloadCompleteAction = appendDownloadCompleteAction;
|
||||
const withReactNativeBlobUtil = (config) => {
|
||||
config = config_plugins_1.AndroidConfig.Permissions.withPermissions(config, [
|
||||
"android.permission.READ_EXTERNAL_STORAGE",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||
"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION",
|
||||
// Wifi-only mode.
|
||||
"android.permission.ACCESS_NETWORK_STATE",
|
||||
]);
|
||||
config = config_plugins_1.withAndroidManifest(config, (config) => {
|
||||
config.modResults = appendDownloadCompleteAction(config.modResults);
|
||||
return config;
|
||||
});
|
||||
return config;
|
||||
};
|
||||
exports.default = config_plugins_1.createRunOncePlugin(withReactNativeBlobUtil, pkg.name, pkg.version);
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('expo-module-scripts/jest-preset-plugin');
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@config-plugins/react-native-blob-util",
|
||||
"version": "0.0.0",
|
||||
"description": "Config plugin to auto configure react-native-blob-util on prebuild",
|
||||
"main": "build/withReactNativeBlobUtil.js",
|
||||
"types": "build/withReactNativeBlobUtil.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": [
|
||||
"react-native-blob-util",
|
||||
"react-native",
|
||||
"expo"
|
||||
],
|
||||
"jest": {
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`appendDownloadCompleteAction should return manifest 1`] = `
|
||||
"<manifest xmlns:android=\\"http://schemas.android.com/apk/res/android\\" package=\\"com.bacon.yolo68\\">
|
||||
<application android:name=\\".MainApplication\\" android:label=\\"@string/app_name\\" android:icon=\\"@mipmap/ic_launcher\\" android:roundIcon=\\"@mipmap/ic_launcher_round\\" android:allowBackup=\\"true\\" android:theme=\\"@style/AppTheme\\" android:usesCleartextTraffic=\\"true\\">
|
||||
<activity android:name=\\".MainActivity\\" android:label=\\"@string/app_name\\" android:configChanges=\\"keyboard|keyboardHidden|orientation|screenSize|uiMode\\" android:launchMode=\\"singleTask\\" android:windowSoftInputMode=\\"adjustResize\\" android:theme=\\"@style/Theme.App.SplashScreen\\" android:screenOrientation=\\"portrait\\">
|
||||
<intent-filter>
|
||||
<action android:name=\\"android.intent.action.MAIN\\"/>
|
||||
<action android:name=\\"android.intent.action.DOWNLOAD_COMPLETE\\"/>
|
||||
<category android:name=\\"android.intent.category.LAUNCHER\\"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name=\\"android.intent.action.VIEW\\"/>
|
||||
<category android:name=\\"android.intent.category.DEFAULT\\"/>
|
||||
<category android:name=\\"android.intent.category.BROWSABLE\\"/>
|
||||
<data android:scheme=\\"com.bacon.yolo68\\"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=\\"com.facebook.react.devsupport.DevSettingsActivity\\"/>
|
||||
</application>
|
||||
</manifest>"
|
||||
`;
|
||||
@@ -0,0 +1,38 @@
|
||||
import { AndroidConfig, XML } from "@expo/config-plugins";
|
||||
import { appendDownloadCompleteAction } from "../withReactNativeBlobUtil";
|
||||
|
||||
const fixture = `
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.bacon.yolo68">
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
|
||||
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="com.bacon.yolo68"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
|
||||
</application>
|
||||
</manifest>`;
|
||||
|
||||
describe(appendDownloadCompleteAction, () => {
|
||||
it("should return manifest", async () => {
|
||||
const manifest = (await XML.parseXMLAsync(fixture)) as any;
|
||||
appendDownloadCompleteAction(manifest);
|
||||
const firstResults = XML.format(manifest);
|
||||
expect(firstResults).toMatchSnapshot();
|
||||
expect(firstResults).toMatch(
|
||||
/<action android:name="android\.intent\.action\.DOWNLOAD_COMPLETE"\/>/
|
||||
);
|
||||
|
||||
// Doesn't mutate original.
|
||||
appendDownloadCompleteAction(manifest);
|
||||
const secondResults = XML.format(manifest);
|
||||
expect(secondResults).toMatch(firstResults);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
AndroidConfig,
|
||||
ConfigPlugin,
|
||||
createRunOncePlugin,
|
||||
withAndroidManifest,
|
||||
} from "@expo/config-plugins";
|
||||
|
||||
let pkg: { name: string; version?: string } = {
|
||||
name: "react-native-blob-util",
|
||||
};
|
||||
try {
|
||||
pkg = require("react-native-blob-util/package.json");
|
||||
} catch {
|
||||
// empty catch block
|
||||
}
|
||||
|
||||
export function appendDownloadCompleteAction(
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest
|
||||
): AndroidConfig.Manifest.AndroidManifest {
|
||||
if (!Array.isArray(androidManifest.manifest.application)) {
|
||||
return androidManifest;
|
||||
}
|
||||
|
||||
for (const application of androidManifest.manifest.application) {
|
||||
for (const activity of application.activity || []) {
|
||||
if (activity?.$?.["android:launchMode"] === "singleTask") {
|
||||
for (const intentFilter of activity["intent-filter"] || []) {
|
||||
const isLauncher = intentFilter.category?.some(
|
||||
(action) =>
|
||||
action.$["android:name"] === "android.intent.category.LAUNCHER"
|
||||
);
|
||||
if (!isLauncher) continue;
|
||||
|
||||
intentFilter.action = intentFilter.action || [];
|
||||
const hasDownloadCompleteAction = intentFilter.action.some(
|
||||
(action) =>
|
||||
action.$["android:name"] ===
|
||||
"android.intent.action.DOWNLOAD_COMPLETE"
|
||||
);
|
||||
if (!hasDownloadCompleteAction) {
|
||||
intentFilter.action.push({
|
||||
$: {
|
||||
"android:name": "android.intent.action.DOWNLOAD_COMPLETE",
|
||||
},
|
||||
});
|
||||
return androidManifest;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return androidManifest;
|
||||
}
|
||||
|
||||
const withReactNativeBlobUtil: ConfigPlugin = (config) => {
|
||||
config = AndroidConfig.Permissions.withPermissions(config, [
|
||||
"android.permission.READ_EXTERNAL_STORAGE",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||
"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION",
|
||||
// Wifi-only mode.
|
||||
"android.permission.ACCESS_NETWORK_STATE",
|
||||
]);
|
||||
|
||||
config = withAndroidManifest(config, (config) => {
|
||||
config.modResults = appendDownloadCompleteAction(config.modResults);
|
||||
return config;
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
export default createRunOncePlugin(
|
||||
withReactNativeBlobUtil,
|
||||
pkg.name,
|
||||
pkg.version
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "expo-module-scripts/tsconfig.plugin",
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
|
||||
}
|
||||
@@ -4,7 +4,7 @@ Config plugin to auto configure react-native-callkeep when the native code is ge
|
||||
|
||||
### Add the package to your npm dependencies
|
||||
|
||||
> Tested against Expo SDK 41
|
||||
> Tested against Expo SDK 43
|
||||
|
||||
```
|
||||
yarn add react-native-callkeep @config-plugins/react-native-callkeep
|
||||
|
||||
+2
-1
@@ -92,7 +92,7 @@ const withCallkeep = (config) => {
|
||||
config = withAndroidManifestService(config);
|
||||
return config;
|
||||
};
|
||||
exports.withXcodeLinkBinaryWithLibraries = (config, { library, status }) => {
|
||||
const withXcodeLinkBinaryWithLibraries = (config, { library, status }) => {
|
||||
return config_plugins_1.withXcodeProject(config, (config) => {
|
||||
const options = status === "optional" ? { settings: { ATTRIBUTES: ["Weak"] } } : {};
|
||||
const target = config_plugins_1.IOSConfig.XcodeUtils.getApplicationNativeTarget({
|
||||
@@ -106,4 +106,5 @@ exports.withXcodeLinkBinaryWithLibraries = (config, { library, status }) => {
|
||||
return config;
|
||||
});
|
||||
};
|
||||
exports.withXcodeLinkBinaryWithLibraries = withXcodeLinkBinaryWithLibraries;
|
||||
exports.default = withCallkeep;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@config-plugins/react-native-callkeep",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"description": "Config plugin to auto configure callkeep on prebuild",
|
||||
"main": "build/withCallkeep.js",
|
||||
"types": "build/withCallkeep.d.ts",
|
||||
@@ -26,7 +26,7 @@
|
||||
"preset": "expo-module-scripts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/config-plugins": "^3.0.3"
|
||||
"@expo/config-plugins": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expo-module-scripts": "^2.0.0"
|
||||
|
||||
Loaded 100 of 142 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user