Compare commits

..
Author SHA1 Message Date
Evan Bacon 754fb08327 Merge branch 'master' into @evanbacon/e2e-tests 2021-12-14 21:13:34 -07:00
Evan Bacon 7a7b88b735 fix packages 2021-12-14 16:40:29 -07:00
Evan Bacon 954cc4c189 Merge pull request #30 from giautm/giautm/fix-metro-bundle
chore: added start-metro script
2021-12-14 16:39:43 -07:00
Giau. Tran Minh dcf61a9b95 chore: update script 2021-12-15 05:11:22 +07:00
Giau. Tran Minh 4a7d707b20 chore: added start-metro script
I don't known why, but iOS release build still crashed on start. Look like it related to expo-updates module.

```
Application Specific Information:
dyld4 config: DYLD_ROOT_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot
CoreSimulator 776.4 - Device: iPhone 13 Pro (152AB63A-3F82-4D26-9AE3-36207700E62C) - Runtime: iOS 15.0 (19A339) - DeviceType: iPhone 13 Pro
terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'expo-updates is enabled, but no valid URL is configured under EXUpdatesURL. If you are making a release build for the first time, make sure you have run `expo publish` at least once.'
dyld4 config: DYLD_ROOT_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot
abort() called
```
2021-12-15 05:08:06 +07:00
Evan Bacon eedb489416 Publish @config-plugins/[email protected] 2021-12-14 11:40:03 -07:00
Evan Bacon 3091016389 Merge pull request #29 from expo/@evanbacon/react-native-google-cast/add-44-support
[react-native-google-cast] Add support for SDK 44 AppDelegate
2021-12-14 11:38:37 -07:00
Evan Bacon 4f9e566e4b Add support for SDK 44 AppDelegate 2021-12-13 22:14:37 -07:00
Evan Bacon 027a1fdb1e Merge pull request #22 from expo/@evanbacon/ffmpeg/create-package
Created @config-plugins/ffmpeg-kit-react-native
2021-12-01 18:10:47 -07:00
Evan Bacon ca6b70dc7a Merge branch 'master' into @evanbacon/ffmpeg/create-package 2021-12-01 18:03:41 -07:00
Evan Bacon b96cb784b9 Hide git build folders 2021-12-01 18:03:31 -07:00
Evan Bacon d44a0513a3 Merge branch 'master' into @evanbacon/ffmpeg/create-package 2021-12-01 18:01:30 -07:00
Evan Bacon f396c0eca2 Publish @config-plugins/[email protected] 2021-11-30 14:40:09 -07:00
Evan Bacon b72c445561 Merge pull request #23 from expo/@evanbacon/react-native-pdf/create
Created react-native-blob-util and react-native-pdf plugins
2021-11-30 13:55:34 -07:00
Evan Bacon b6d7a269c3 Merge branch 'master' into @evanbacon/react-native-pdf/create 2021-11-30 13:51:40 -07:00
Evan Bacon b1c48798a2 Create test.yml 2021-11-30 13:51:28 -07:00
Evan Bacon c0caaa117e Update test.yml 2021-11-30 13:50:09 -07:00
Evan Bacon 24b6abbf63 Merge branch 'master' into @evanbacon/ffmpeg/create-package 2021-11-30 13:49:18 -07:00
Evan Bacon b46f25e204 Merge pull request #25 from expo/@evanbacon/repo/add-jest-test-action
[repo] Add continuous jest test action
2021-11-30 13:48:49 -07:00
Evan Bacon 7d5e4a3083 Update test.yml 2021-11-30 13:37:07 -07:00
Evan Bacon 581dd45715 fix tests 2021-11-30 13:35:52 -07:00
Evan Bacon a7e4206ffa Merge branch 'master' into @evanbacon/repo/add-jest-test-action 2021-11-30 13:34:28 -07:00
Evan Bacon 05fe7bf107 Update package.json 2021-11-30 13:34:25 -07:00
Evan Bacon fa107895b6 Update detox.config.js 2021-11-29 17:18:39 -07:00
Evan Bacon d31b0273cf [WIP][NO-MERGE] 2021-11-29 17:01:02 -07:00
Evan Bacon cd9a188413 Revert "added i18n-js back"
This reverts commit 4c1607a2cf.
2021-11-29 16:59:17 -07:00
Evan Bacon 4c1607a2cf added i18n-js back
update tests

[WIP][NO-MERGE] Remove extra packages to speed up tests
2021-11-29 16:59:03 -07:00
Evan Bacon 3a7a22be0e Update test.yml 2021-11-29 16:37:38 -07:00
Evan Bacon 978448646d Update test.yml 2021-11-29 16:37:20 -07:00
Evan Bacon 3703b47b53 Merge pull request #24 from expo/@evanbacon/detox/improved-monorepo-support
[detox] improved monorepo support
2021-11-29 16:36:59 -07:00
Evan Bacon e370254d10 Add continuous jest test action 2021-11-29 16:35:26 -07:00
Evan Bacon 2f358c25e8 Try new monorepo imports 2021-11-29 16:30:21 -07:00
Evan Bacon 5caee7a24c Support monorepos in detox app build gradle proguard rules 2021-11-29 16:28:27 -07:00
Evan Bacon 3ac42ac2fa Support monorepos in detox maven gradle import 2021-11-29 16:19:48 -07:00
Evan Bacon d2eb511e1d Update e2e.yml 2021-11-29 14:38:53 -07:00
Evan Bacon f62a941bc6 Update e2e.yml 2021-11-29 14:38:30 -07:00
Evan Bacon d784d14486 Update app.json 2021-11-29 11:44:27 -07:00
Evan Bacon 7b0d0225f9 Update yarn.lock 2021-11-29 11:43:49 -07:00
Evan Bacon c1d25701ca Merge branch 'master' into @evanbacon/e2e-tests 2021-11-29 11:43:10 -07:00
Evan Bacon 529410be0f Created react-native-blob-util and react-native-pdf plugins 2021-11-26 18:39:41 -07:00
Evan Bacon 04ef242347 Added small demo 2021-11-26 17:16:51 -07:00
Evan Bacon fe00996dbd Added ffmpeg to example 2021-11-26 17:02:09 -07:00
Evan Bacon ba84473db8 Created ffmpeg-kit-react-native plugin 2021-11-26 16:56:33 -07:00
Evan Bacon 359cbb455d Update README.md 2021-11-23 20:23:56 -07:00
Evan Bacon 909db3c158 Merge pull request #21 from expo/@evanbacon/repo/bump-to-43
[repo] Bump to SDK 43
2021-11-23 15:51:11 -07:00
Evan Bacon 0aaf6d07d6 fix deps 2021-11-23 15:42:17 -07:00
Evan Bacon 81857f8938 Added CI updates 2021-11-23 15:37:03 -07:00
Evan Bacon dc86073608 Bump versions 2021-11-23 15:15:08 -07:00
Evan Bacon 8dd7006769 Bump detox 2021-11-23 15:13:33 -07:00
Evan Bacon f62f4f59ba wip 2021-11-23 14:51:51 -07:00
Evan Bacon 63257cfa20 bump 2021-11-23 14:40:53 -07:00
Evan Bacon b6b9ce09c1 ios running 2021-11-23 14:36:17 -07:00
Evan Bacon 4c3cd1413b Fixed fs errors 2021-11-23 14:22:55 -07:00
Evan Bacon 5833dd9ea3 Bump notices for Expo 43 2021-11-23 14:10:05 -07:00
Evan Bacon 49448a8974 Merge pull request #20 from expo/@evanbacon/expo/[email protected]
Bump to Expo SDK 43
2021-11-23 14:02:33 -07:00
Evan Bacon 780c27319d Updated to SDK 43 and iOS 15 2021-10-22 15:43:00 -06:00
Evan Bacon e3acf06968 expo upgrade 2021-10-22 15:02:50 -06:00
Evan Bacon 7cce4a464e Bump packages 2021-10-22 14:58:36 -06:00
Evan Bacon d214964657 Merge pull request #19 from expo/@evanbacon/react-native-webrtc/disable-bitcode
[react-native-webrtc] Add step to disable bitcode for release iOS builds
2021-10-22 14:40:07 -06:00
Evan Bacon 3cd73f32a2 Add step to disable bitcode for release iOS builds 2021-10-22 14:34:22 -06:00
Evan Bacon f96f9bdc73 Update README.md 2021-10-19 19:11:04 -06:00
Evan Bacon f0d957456e Publish @config-plugins/[email protected]
Publish @config-plugins/[email protected]
2021-08-04 14:34:44 -06:00
Evan Bacon c2a78787e8 Merge pull request #14 from expo/@evanbacon/react-native-dynamic-app-icon/updated-packages
Bump image utils and config plugins
2021-08-04 14:32:22 -06:00
Evan Bacon bd5710b412 Bump image utils and config plugins 2021-08-04 14:30:24 -06:00
Evan Bacon f15f5fc7ed Publish @config-plugins/[email protected] 2021-07-21 00:00:23 -06:00
Evan Bacon 2546094be7 Merge pull request #13 from expo/@evanbacon/ble-plx/added-peripheral-permission
[react-native-ble-plx] Added peripheral permission
2021-07-20 23:59:03 -06:00
Evan Bacon 38458d2c9f Added peripheral permission 2021-07-20 23:56:56 -06:00
Evan Bacon 619b2fb02c Merge pull request #10 from expo/@evanbacon/react-native-quick-actions/create
Created react-native-quick-action plugin
2021-07-12 23:40:15 -06:00
Evan Bacon 5359120dbd Improved CI 2021-07-03 18:43:11 -06:00
Evan Bacon ed6b3a0ec0 Update detox.config.js 2021-07-03 18:30:03 -06:00
Evan Bacon efd3284272 remove webrtc and google chrome 2021-07-03 18:19:46 -06:00
Evan Bacon 46bc48726c Update App-e2e.ts 2021-07-03 18:00:57 -06:00
Evan Bacon ff47e492e4 Update App-e2e.ts 2021-07-03 17:40:47 -06:00
Evan Bacon be969274b9 Update app.json 2021-07-03 17:21:36 -06:00
Evan Bacon 359c1cfcb5 added missing resize mode 2021-07-03 17:15:17 -06:00
Evan Bacon 6c3fc5dff7 Update e2e.yml 2021-07-03 17:12:56 -06:00
Evan Bacon 99d3927680 Update e2e.yml 2021-07-03 17:06:40 -06:00
Evan Bacon 55482dae5f Create .ruby-version 2021-07-03 17:04:15 -06:00
Evan Bacon 395599abfa Added e2e tests 2021-07-03 16:59:15 -06:00
142 changed files with 4709 additions and 1319 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
packages/*/build/** -diff linguist-generated
packages/*/plugin/build/** -diff linguist-generated
packages/@*/*/build/** -diff linguist-generated
+155
View File
@@ -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
+60
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
2.7.0
+9 -7
View File
@@ -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
View File
@@ -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';
+6
View File
@@ -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
View File
@@ -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"
]
}
}
+5 -5
View File
@@ -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",
+7 -14
View File
@@ -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();
});
});
+18 -8
View File
@@ -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
View File
@@ -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,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

+5 -2
View File
@@ -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
View File
@@ -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
}
+30
View File
@@ -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'
+39
View File
@@ -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;
}
+41
View File
@@ -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",
},
});
+1 -1
View File
@@ -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/).
+2 -2
View File
@@ -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"
+12 -1
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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;
+2 -2
View File
@@ -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(
+2 -2
View File
@@ -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,
+53 -11
View File
@@ -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;
+3
View File
@@ -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,
+4 -3
View File
@@ -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
View File
@@ -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 };
}
@@ -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
View File
@@ -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
View File
@@ -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 {};
+43
View File
@@ -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
View File
@@ -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"
}
}
@@ -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"
`;
@@ -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();
});
});
@@ -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__/*"]
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3 -3
View File
@@ -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 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
@@ -1,4 +0,0 @@
import { ConfigPlugin } from '@expo/config-plugins';
export declare const withBluetoothAlwaysPermission: ConfigPlugin<{
bluetoothAlwaysPermission?: string | false;
}>;
-16
View File
@@ -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
View File
@@ -0,0 +1,5 @@
import { ConfigPlugin } from "@expo/config-plugins";
export declare const withBluetoothPermissions: ConfigPlugin<{
bluetoothAlwaysPermission?: string | false;
bluetoothPeripheralPermission?: string | false;
}>;
+25
View File
@@ -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;
+2 -2
View File
@@ -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"
+4 -3
View File
@@ -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');
+15
View File
@@ -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
View File
@@ -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
View File
@@ -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"
}
}
@@ -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__/*"]
}
+1 -1
View File
@@ -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
View File
@@ -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;
+2 -2
View File
@@ -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