Compare commits

..
32 Commits
Author SHA1 Message Date
Mike Grabowski 07f48ebf4a [0.49.5] Bump version numbers 2017-10-27 11:55:05 -07:00
Adam Miskiewicz 84241fbcdc Make Animated.SpringAnimation JS compatible with Expo SDK 22 (CRNA) native code (#16513) 2017-10-25 15:12:26 +02:00
Mike Grabowski ee68b113ff [0.49.4] Bump version numbers 2017-10-13 08:53:48 +02:00
Mike Grabowski 4555b06ffd Fix broken releases script
Summary:
Fails on my machine due to fact that `replace` returns an instance of a String, rather than an instance of ShellString (that includes `to` on its prototype).

Solution is to use an explicit `writeFileSync`. You can see that change in the wild on 0.50-stable branch.

CC janicduplessis (edit by hramos)
Closes https://github.com/facebook/react-native/pull/16303

Differential Revision: D6031331

Pulled By: hramos

fbshipit-source-id: 41c583d53df75bea1a55fa19174d912e414209c0
2017-10-13 08:53:31 +02:00
Alex Dvornikov dd63cbf5df Add a check in InitializeCore.js that PlatformConstants native module is present
Summary: In some enviroments PlatformConstants native module may not be presented in a project, which results in a call to undefined property and a RedBox

Reviewed By: javache

Differential Revision: D5960879

fbshipit-source-id: 80aecbe2f2a61cb410abd5f0dce8ba855e166991
2017-10-13 08:53:09 +02:00
James Ide afdd0d5d93 [0.49.3] Bump version numbers 2017-10-09 02:03:33 -07:00
Brian Vaughn 4cb4a71656 Fixed runtime error with ProgressBarAndroid
Reviewed By: yungsters

Differential Revision: D5857305

fbshipit-source-id: 2fc20a848fa4dce5c1ac3fb7e986536618e25548
2017-10-09 02:03:04 -07:00
James Ide 7b7b491e50 [0.49.2] Bump version numbers 2017-10-07 02:58:14 -07:00
Naman Goel 45a48271b1 Fix Flow errors in ListViewData source.
Differential Revision: D5839414

fbshipit-source-id: 7a0c7b3d27be728c74e08d3a8209deb3ca68dd63
2017-10-07 02:56:55 -07:00
James Ide cd81eff5b8 [0.49.1] Bump version numbers 2017-10-03 19:08:02 -07:00
James Ide 6f232424e6 Set fishhook's header_dir to "fishhook"
Summary:
This lets us import fishhook like `<fishhook/fishhook.h>`.
Closes https://github.com/facebook/react-native/pull/16192

Differential Revision: D5971639

Pulled By: shergin

fbshipit-source-id: c46503953a08a74bdd10ed35cbd30029f1666c7d
2017-10-03 19:07:10 -07:00
Mike Grabowski 35717c773a [0.49.0] Bump version numbers 2017-10-03 17:42:52 +02:00
James Ide af89654699 [0.49.0-rc.6] Bump version numbers 2017-09-28 00:37:17 -07:00
Janic Duplessis 776bd34eb3 Validate that JS and Native code versions match for RN releases
Summary:
Basic implementation of the proposal in #15271

Note that this should not affect facebook internally since they are not using OSS releases.

Points to consider:
- How strict should the version match be, right now I just match exact versions.
- Wasn't able to use haste for ReactNativeVersion because I was getting duplicate module provider caused by the template file in scripts/versiontemplates. I tried adding the scripts folder to modulePathIgnorePatterns in package.json but that didn't help.
- Redscreen vs. warning, I think warning is useless because if the app crashes you won't have time to see the warning.
- Should the check and native modules be __DEV__ only?

**Test plan**
Tested that it works when version match and that it redscreens when versions don't before getting other errors on Android and iOS.
Closes https://github.com/facebook/react-native/pull/15518

Differential Revision: D5813551

Pulled By: hramos

fbshipit-source-id: 901757e25724b0f22bf39de172b56309d0dd5a95
2017-09-27 22:28:43 -07:00
James Ide ac4a214eeb [0.49.0-rc.5] Bump version numbers 2017-09-18 22:59:03 -07:00
Tim Wang 1f85e21464 Add BlobModule native module mock to jest setup
Summary:
React Native v0.48.0 shipped `WebSocketModule` support with `BlobModule` as dependency. But `BlobModule` is not mocked in jest which will cause render tests failed.

Reference implantation: [BlobModule.java](https://github.com/facebook/react-native/blob/ed903099b42259958d8a6eb3af1a6460c1bc8b2c/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobModule.java)

Related PR: #11417
Related issue: #15810

Passed CI tests.
Need render a component in jest with WebSocketModule as dependency.
Closes https://github.com/facebook/react-native/pull/15843

Differential Revision: D5783263

Pulled By: shergin

fbshipit-source-id: 2386692f4a644796da2fd66b3135da9f5911663e
2017-09-18 22:55:36 -07:00
James Ide a41995ff5c [0.49.0-rc.4] Bump version numbers 2017-09-14 12:11:13 -07:00
Ben Roth 6d64892c8a Fix crash when trying to load photo library assets with nil image url
Summary:
This avoids a crash when we try to load a PHAsset with nil image url. Specifically, the following condition evaluates to true when `imageURL` is nil:

```objc
if ([imageURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) {
    assetID = [imageURL absoluteString];
    results = [PHAsset fetchAssetsWithALAssetURLs:@[imageURL] options:nil];
}
```

The crash will be "attempt to insert nil object from objects[0]" when we build the `@[imageURL]` array literal.

We've seen this emerge as a very common crash among Expo users, so I wanted to at least provide a clear error message instead of terminating the app.

Load an image from the photo library with a nil request url.
Closes https://github.com/facebook/react-native/pull/15952

Differential Revision: D5835219

Pulled By: ericnakagawa

fbshipit-source-id: 7be00a15e674a0905cf5c27c526ce9085d1b308f
2017-09-14 12:10:14 -07:00
Adam Miskiewicz 5d95e9950c Refactor how symlinks are discovered in local-cli, support scoped modules
Summary:
This PR refactors the symlink finding logic in local-cli in order to support nested symlinked modules as well as symlinked scoped NPM modules.

Run tests, or try project with `npm link`'ed or `yarn link`'ed dependencies.
Closes https://github.com/facebook/react-native/pull/15776

Reviewed By: cpojer

Differential Revision: D5823008

Pulled By: jeanlauliac

fbshipit-source-id: f2daeceef37bed2f8a136a0a5918730f9832884c
2017-09-14 12:10:02 -07:00
Andrew Jack 6b39d644ea Android Oreo (8.0) Support
Summary:
Apps targeting Android 8.0 (API level 26) cannot use `TYPE_SYSTEM_OVERLAY ` and `TYPE_SYSTEM_OVERLAY `. Targeting 26 will cause the app to crash when in `DEV_MODE`.

https://developer.android.com/about/versions/oreo/android-8.0-changes.html#cwt

This PR replaces uses of these overlay flags with the new `TYPE_APPLICATION_OVERLAY` when running on a device with Android 8.0 or later.

When using `TYPE_APPLICATION_OVERLAY` it still requires the `SYSTEM_ALERT_WINDOW` permission, just like previous android versions.
https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#TYPE_APPLICATION_OVERLAY

https://github.com/AndrewJack/react-native-android-oreo tested here
Closes https://github.com/facebook/react-native/pull/15601

Reviewed By: achen1

Differential Revision: D5801619

Pulled By: shergin

fbshipit-source-id: 27d1b9bb64018e7f12f9c3d3d222f1fda468b124
2017-09-14 11:06:29 -07:00
James Ide dff572c174 [0.49.0-rc.3] Bump version numbers 2017-09-11 15:27:51 -07:00
Pieter De Baets af3e1d4a86 Improve RCTCxxBridge invalidation
Reviewed By: fromcelticpark

Differential Revision: D5536063

fbshipit-source-id: b0f19bebea839c7da84890c338ad4d38293bc99c
2017-09-11 15:23:48 -07:00
Mike Grabowski 897a3a5d5e [0.49.0-rc.2] Bump version numbers 2017-09-08 11:37:01 +02:00
Héctor Ramos 0fea3515a1 Comment out failing buck install command
Summary:
Workaround for #15726.
Closes https://github.com/facebook/react-native/pull/15817

Differential Revision: D5767831

Pulled By: hramos

fbshipit-source-id: f0324ed4052007648ac23f9015069a97f005d19b
2017-09-08 00:04:35 -07:00
Héctor Ramos 80e8efe8e7 Comment out Flow tests
Summary:
Commenting these out until we can stop breaking the OSS builds.
Closes https://github.com/facebook/react-native/pull/15859

Differential Revision: D5789816

Pulled By: hramos

fbshipit-source-id: f3b2ecd0cbcb4dbe5e35697a925b2a8bd8397afe
2017-09-08 00:03:35 -07:00
James Ide d1888650c5 [0.49.0-rc.1] Bump version numbers 2017-09-07 14:49:21 -07:00
James Ide f65b9e883a Change intValue -> integerValue in RCTMultipartStreamReader
Summary:
We assign the value to an NSInteger so we should convert the NSString to an NSInteger instead of an int.

Build the app in Xcode and run it.
Closes https://github.com/facebook/react-native/pull/15806

Differential Revision: D5767118

Pulled By: shergin

fbshipit-source-id: b310511f13f5f4026d595a219d69811801d313c2
2017-09-07 14:17:01 -07:00
Gilad Novik aa0b2ad1cd Fix unrecognized selector error
Summary:
Fixes "[NSTaggedPointerString unsignedIntValue]: unrecognized selector" error
Closes https://github.com/facebook/react-native/pull/15755

Differential Revision: D5763628

Pulled By: shergin

fbshipit-source-id: 6e1d1b1ae6bc63e07112b9dddcdf8e6f812a7f51
2017-09-07 14:16:54 -07:00
Neo 0a319978ff fix fontWeight regression
Summary:
fix the regression I mentioned in https://github.com/facebook/react-native/pull/15162#issuecomment-319696706

as no one is working on this, I take the step, although I know nothing about Objective C

I find the key point is that the keys in `NSDictionary` are not ordered as presented, it's a hash table, so no grantee on keys order, so I create a new array to do that, then it will check `ultralight` before `light` and `semibold` before `bold`
Closes https://github.com/facebook/react-native/pull/15825

Differential Revision: D5782142

Pulled By: shergin

fbshipit-source-id: 5346b0cb263e535c0b445e7a2912c452573248a5
2017-09-07 14:16:44 -07:00
Janic Duplessis 29e5deb85f Fix scroll events getting skipped on Android
Summary:
This code related to velocity would cause some scroll events to be skipped and caused some jitter for sticky headers. Not sure if there is a better fix but removing this does fix missing events and the velocity calculation still seems to be working.

**Test plan**
Tested that sticky headers now work properly on Android, tested that velocity calculation still seem to work.
Closes https://github.com/facebook/react-native/pull/15761

Differential Revision: D5760820

Pulled By: sahrens

fbshipit-source-id: 562b5f606bdc5452ca3d85efb5e0e3e7db224891
2017-09-07 14:16:38 -07:00
Héctor Ramos 031feb6942 Update IntegrationWithExistingApps.md 2017-09-05 10:22:45 -07:00
Mike Grabowski d82138ff91 [0.49.0-rc.0] Bump version numbers 2017-09-01 17:28:27 +02:00
5660 changed files with 240139 additions and 531219 deletions
+2 -4
View File
@@ -1,14 +1,12 @@
[android]
target = android-29
target = Google Inc.:Google APIs:23
[download]
max_number_of_retries = 3
[maven_repositories]
central = https://repo1.maven.org/maven2
google = https://maven.google.com/
jcenter = https://jcenter.bintray.com/
[alias]
rntester = //packages/rn-tester/android/app:app
rntester = //RNTester/android/app:app
-81
View File
@@ -1,81 +0,0 @@
# Docker Test Environment
This is a high-level overview of the test configuration using Docker.
It explains how to run the tests locally.
## Docker Installation
It is required to have Docker running on your machine in order to build and run the tests in the Dockerfiles.
See <https://docs.docker.com/engine/installation/> for more information on how to install.
## Convenience Scripts
We have added a number of default run scripts to the `package.json` file to simplify building and running your tests.
### Configuring Docker Images
The following two scripts need to be run first before you can move on to testing:
- `yarn run docker-setup-android`: Pulls down the React Native Community Android image that serves as a base image when building the actual test image.
- `yarn run docker-build-android`: Builds a test image with the latest dependencies and React Native library code, including a compiled Android test app.
### Running Tests
Once the test image has been built, it can be used to run our Android tests.
- `yarn run test-android-run-unit` runs the unit tests, as defined in `scripts/run-android-docker-unit-tests.sh`.
- `yarn run test-android-run-e2e` runs the end to end tests, as defined in `scripts/run-ci-e2e-tests.sh`.
- `yarn run test-android-run-instrumentation` runs the instrumentation tests, as defined in `scripts/run-android-docker-instrumentation-tests.sh`.
#### Instrumentation Tests
The instrumentation test script accepts the following flags in order to customize the execution of the tests:
`--filter` - A regex that filters which instrumentation tests will be run. (Defaults to .*)
`--package` - Name of the java package containing the instrumentation tests (Defaults to com.facebook.react.tests)
`--path` - Path to the directory containing the instrumentation tests. (Defaults to ./ReactAndroid/src/androidTest/java/com/facebook/react/tests)
`--retries` - Number of times to retry a failed test before declaring a failure (Defaults to 2)
For example, if locally you only wanted to run the InitialPropsTestCase, you could do the following:
`yarn run test-android-run-instrumentation -- --filter="InitialPropsTestCase"`
## Detailed Android Setup
There are two Dockerfiles for use with the Android codebase.
The base image used to build `reactnativecommunity/react-native-android` is located in the https://github.com/react-native-community/docker-android GitHub repository.
It contains all the necessary prerequisites required to run the React Android tests.
It is separated out into a separate Dockerfile because these are dependencies that rarely change and also because it is quite a beastly image since it contains all the Android dependencies for running Android and the emulators (~9GB).
The good news is you should rarely have to build or pull down the base image!
All iterative code updates happen as part of the `Dockerfile.android` image build.
Lets break it down...
First, you'll need to pull the base image.
You can use `docker pull` to grab the latest version of the `reactnativecommunity/react-native-android` base image.
This is what you get when you run `yarn run docker-setup-android`.
This will take quite some time depending on your connection and you need to ensure you have ~10GB of free disk space.
Once you have downloaded the base image, the test image can be built using `docker build -t reactnativeci/android -f ./.circleci/Dockerfiles/Dockerfile.android .`. This is what `yarn run docker-build-android` does. Note that the `-t` flag is how you tell Docker what to name this image locally. You can then use `docker run -t reactnativeci/android` to run this image.
Now that you've built the test image, you can run unit tests using what you've learned so far:
```bash
docker run --cap-add=SYS_ADMIN -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-android-docker-unit-tests.sh
```
> Note: `--cap-add=SYS_ADMIN` flag is required for the `.circleci/Dockerfiles/scripts/run-android-docker-unit-tests.sh` and `.circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh` in order to allow the remounting of `/dev/shm` as writeable so the `buck` build system may write temporary output to that location.
Every time you make any modifications to the codebase, including changes to the test scripts inside `.circleci/Dockerfiles/scripts`, you should re-run the `docker build ...` command in order for your updates to be included in your local docker test image.
For rapid iteration, it's useful to keep in mind that Docker can pass along arbitrary commands to an image.
For example, you can alternatively use Gradle in this manner:
```bash
docker run --cap-add=SYS_ADMIN -it reactnativeci/android ./gradlew RNTester:android:app:assembleRelease
```
-53
View File
@@ -1,53 +0,0 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
# This image builds upon the React Native Community Android image:
# https://github.com/react-native-community/docker-android
#
# The base image is expected to remain relatively stable, and only
# needs to be updated when major dependencies such as the Android
# SDK or NDK are updated.
#
# In this Android Test image, we download the latest dependencies
# and build a Android application that can be used to run the
# tests specified in the scripts/ directory.
#
FROM reactnativecommunity/react-native-android:2.1
LABEL Description="React Native Android Test Image"
LABEL maintainer="Héctor Ramos <hector@fb.com>"
# set default environment variables
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
ADD .buckconfig /app/.buckconfig
ADD .buckjavaargs /app/.buckjavaargs
ADD Libraries /app/Libraries
ADD ReactAndroid /app/ReactAndroid
ADD ReactCommon /app/ReactCommon
ADD React /app/React
ADD keystores /app/keystores
ADD packages/react-native-codegen /app/packages/react-native-codegen
ADD tools /app/tools
WORKDIR /app
RUN buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
RUN buck fetch ReactAndroid/src/test/...
RUN buck fetch ReactAndroid/src/androidTest/...
RUN buck build ReactAndroid/src/main/java/com/facebook/react
RUN buck build ReactAndroid/src/main/java/com/facebook/react/shell
ADD . /app
RUN yarn
RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog
RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1
@@ -1,40 +0,0 @@
#!/bin/bash
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# for buck gen
mount -o remount,exec /dev/shm
AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
# create virtual device
echo no | android create avd -n "$AVD_UUID" -f -t android-21 --abi default/armeabi-v7a
# emulator setup
emulator64-arm -avd $AVD_UUID -no-skin -no-audio -no-window -no-boot-anim &
bootanim=""
until [[ "$bootanim" =~ "stopped" ]]; do
sleep 5
bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1)
echo "boot animation status=$bootanim"
done
set -x
# solve issue with max user watches limit
echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches
watchman shutdown-server
# integration tests
# build JS bundle for instrumentation tests
node cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
# build test APK
# shellcheck disable=SC1091
source ./scripts/android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
# run installed apk with tests
node ./.circleci/Dockerfiles/scripts/run-android-ci-instrumentation-tests.js "$*"
exit $?
@@ -1,16 +0,0 @@
#!/bin/bash
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# set default environment variables
UNIT_TESTS_BUILD_THREADS="${UNIT_TESTS_BUILD_THREADS:-1}"
# for buck gen
mount -o remount,exec /dev/shm
set -x
# run unit tests
buck test ReactAndroid/src/test/... --config build.threads="$UNIT_TESTS_BUILD_THREADS"
@@ -1,251 +0,0 @@
#!/bin/bash
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set -ex
# set default environment variables
ROOT=$(pwd)
SCRIPTS=$(pwd)/scripts
RUN_ANDROID=0
RUN_CLI_INSTALL=1
RUN_IOS=0
RUN_JS=0
RETRY_COUNT=${RETRY_COUNT:-2}
AVD_UUID=$(< /dev/urandom tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
ANDROID_NPM_DEPS="appium@1.5.1 mocha@2.4.5 wd@0.3.11 colors@1.0.3 pretty-data2@0.40.1"
CLI_PACKAGE="$ROOT/react-native-cli/react-native-cli-*.tgz"
PACKAGE="$ROOT/react-native-*.tgz"
# solve issue with max user watches limit
echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches
watchman shutdown-server
# retries command on failure
# $1 -- max attempts
# $2 -- command to run
function retry() {
local -r -i max_attempts="$1"; shift
local -r cmd="$*"
local -i attempt_num=1
until $cmd; do
if (( attempt_num == max_attempts )); then
echo "Execution of '$cmd' failed; no more attempts left"
return 1
else
(( attempt_num++ ))
echo "Execution of '$cmd' failed; retrying for attempt number $attempt_num..."
fi
done
}
# parse command line args & flags
while :; do
case "$1" in
--android)
RUN_ANDROID=1
shift
;;
--ios)
RUN_IOS=1
shift
;;
--js)
RUN_JS=1
shift
;;
--skip-cli-install)
RUN_CLI_INSTALL=0
shift
;;
*)
break
esac
done
function e2e_suite() {
cd "$ROOT"
if [ $RUN_ANDROID -eq 0 ] && [ $RUN_IOS -eq 0 ] && [ $RUN_JS -eq 0 ]; then
echo "No e2e tests specified!"
return 0
fi
# create temp dir
TEMP_DIR=$(mktemp -d /tmp/react-native-XXXXXXXX)
# To make sure we actually installed the local version
# of react-native, we will create a temp file inside the template
# and check that it exists after `react-native init
IOS_MARKER="$(mktemp "$ROOT"/template/ios/HelloWorld/XXXXXXXX)"
ANDROID_MARKER="$(mktemp "$ROOT"/template/android/XXXXXXXX)"
# install CLI
cd react-native-cli
npm pack
cd ..
# can skip cli install for non sudo mode
if [ $RUN_CLI_INSTALL -ne 0 ]; then
if ! npm install -g "$CLI_PACKAGE"
then
echo "Could not install react-native-cli globally, please run in su mode"
echo "Or with --skip-cli-install to skip this step"
return 1
fi
fi
if [ $RUN_ANDROID -ne 0 ]; then
set +ex
# create virtual device
if ! android list avd | grep "$AVD_UUID" > /dev/null; then
echo no | android create avd -n "$AVD_UUID" -f -t android-21 --abi default/armeabi-v7a
fi
# newline at end of adb devices call and first line is headers
DEVICE_COUNT=$(adb devices | wc -l)
((DEVICE_COUNT -= 2))
# will always kill an existing emulator if one exists for fresh setup
if [[ $DEVICE_COUNT -ge 1 ]]; then
adb emu kill
fi
# emulator setup
emulator64-arm -avd "$AVD_UUID" -no-skin -no-audio -no-window -no-boot-anim &
bootanim=""
# shellcheck disable=SC2076
until [[ "$bootanim" =~ "stopped" ]]; do
sleep 5
bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1)
echo "boot animation status=$bootanim"
done
set -ex
if ! ./gradlew :ReactAndroid:installArchives -Pjobs=1 -Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"
then
echo "Failed to compile Android binaries"
return 1
fi
fi
if ! npm pack
then
echo "Failed to pack react-native"
return 1
fi
cd "$TEMP_DIR"
if ! retry "$RETRY_COUNT" react-native init EndToEndTest --version "$PACKAGE" --npm
then
echo "Failed to execute react-native init"
echo "Most common reason is npm registry connectivity, try again"
return 1
fi
cd EndToEndTest
# android tests
if [ $RUN_ANDROID -ne 0 ]; then
echo "Running an Android e2e test"
echo "Installing e2e framework"
if ! retry "$RETRY_COUNT" npm install --save-dev "$ANDROID_NPM_DEPS" --silent >> /dev/null
then
echo "Failed to install appium"
echo "Most common reason is npm registry connectivity, try again"
return 1
fi
cp "$SCRIPTS/android-e2e-test.js" android-e2e-test.js
(
cd android || exit
echo "Downloading Maven deps"
./gradlew :app:copyDownloadableDepsToLibs
)
keytool -genkey -v -keystore android/keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"
node ./node_modules/.bin/appium >> /dev/null &
APPIUM_PID=$!
echo "Starting appium server $APPIUM_PID"
echo "Building app"
buck build android/app
# hack to get node unhung (kill buckd)
if ! kill -9 "$(pgrep java)"
then
echo "could not execute Buck build, is it installed and in PATH?"
return 1
fi
echo "Starting Metro"
npm start >> /dev/null &
SERVER_PID=$!
sleep 15
echo "Executing android e2e test"
if ! retry "$RETRY_COUNT" node node_modules/.bin/_mocha android-e2e-test.js
then
echo "Failed to run Android e2e tests"
echo "Most likely the code is broken"
return 1
fi
# kill packager process
if kill -0 "$SERVER_PID"; then
echo "Killing packager $SERVER_PID"
kill -9 "$SERVER_PID"
fi
# kill appium process
if kill -0 "$APPIUM_PID"; then
echo "Killing appium $APPIUM_PID"
kill -9 "$APPIUM_PID"
fi
fi
# ios tests
if [ $RUN_IOS -ne 0 ]; then
echo "Running ios e2e tests not yet implemented for docker!"
fi
# js tests
if [ $RUN_JS -ne 0 ]; then
# Check the packager produces a bundle (doesn't throw an error)
if ! react-native bundle --max-workers 1 --platform android --dev true --entry-file index.js --bundle-output android-bundle.js
then
echo "Could not build android bundle"
return 1
fi
if ! react-native bundle --max-workers 1 --platform ios --dev true --entry-file index.js --bundle-output ios-bundle.js
then
echo "Could not build iOS bundle"
return 1
fi
# directory cleanup
rm "$IOS_MARKER"
rm "$ANDROID_MARKER"
return 0
}
retry "$RETRY_COUNT" e2e_suite
-5
View File
@@ -1,5 +0,0 @@
# Circle CI
This directory is home to the Circle CI configuration file. Circle is our continuous integration service provider. You can see the overall status of React Native's builds at https://circleci.com/gh/facebook/react-native
You may also see an individual PR's build status by scrolling down to the Checks section in the PR.
-895
View File
@@ -1,895 +0,0 @@
version: 2.1
# -------------------------
# ORBS
# -------------------------
orbs:
win: circleci/windows@2.4.0
# -------------------------
# DEFAULTS
# -------------------------
defaults: &defaults
working_directory: ~/react-native
environment:
- GIT_COMMIT_DESC: git log --format=oneline -n 1 $CIRCLE_SHA1
# The public github tokens are publicly visible by design
- PUBLIC_PULLBOT_GITHUB_TOKEN_A: "a6edf8e8d40ce4e8b11a"
- PUBLIC_PULLBOT_GITHUB_TOKEN_B: "150e1341f4dd9c944d2a"
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: &github_token_a "78a72af35445ca3f8180"
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: &github_token_b "b1a98e0bbd56ff1ccba1"
# -------------------------
# EXECUTORS
# -------------------------
executors:
nodelts:
<<: *defaults
docker:
- image: circleci/node:14
nodeprevlts:
<<: *defaults
docker:
- image: circleci/node:12
reactnativeandroid:
<<: *defaults
docker:
- image: reactnativecommunity/react-native-android:2.1
resource_class: "large"
environment:
- TERM: "dumb"
- ADB_INSTALL_TIMEOUT: 10
- _JAVA_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
- GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"'
- BUILD_THREADS: 2
# Repeated here, as the environment key in this executor will overwrite the one in defaults
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: *github_token_a
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: *github_token_b
reactnativeios:
<<: *defaults
macos:
xcode: &_XCODE_VERSION "12.1.0"
# -------------------------
# COMMANDS
# -------------------------
commands:
restore_cache_checkout:
parameters:
checkout_type:
type: string
default: node
steps:
- restore_cache:
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}-<< parameters.checkout_type >>
setup_artifacts:
steps:
- run:
name: Initial Setup
command: mkdir -p ./reports/{buck,build,junit,outputs}
run_yarn:
steps:
- restore_cache:
keys:
- v4-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
- v4-yarn-cache-{{ arch }}
- run:
name: "Yarn: Install Dependencies"
command: |
# Skip yarn install on metro bump commits as the package is not yet
# available on npm
if [[ $(echo "$GIT_COMMIT_DESC" | grep -c "Bump metro@") -eq 0 ]]; then
yarn install --non-interactive --cache-folder ~/.cache/yarn
fi
- save_cache:
paths:
- ~/.cache/yarn
key: v4-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
install_buck_tooling:
steps:
- restore_cache:
keys:
- v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }}}
- v3-buck-v2019.01.10.01-
- run:
name: Install BUCK
command: |
buck --version
# Install related tooling
if [[ ! -e ~/okbuck ]]; then
git clone https://github.com/uber/okbuck.git ~/okbuck --depth=1
fi
- save_cache:
paths:
- ~/buck
- ~/okbuck
key: v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }}
install_github_bot_deps:
steps:
- run:
name: "Yarn: Install dependencies (GitHub bots)"
command: cd bots && yarn install --non-interactive --cache-folder ~/.cache/yarn
brew_install:
parameters:
package:
description: Homebrew package to install
type: string
steps:
- run:
name: "Brew: Install << parameters.package >>"
command: HOMEBREW_NO_AUTO_UPDATE=1 brew install << parameters.package >> >/dev/null
with_brew_cache_span:
parameters:
steps:
type: steps
steps:
- restore_cache:
keys:
- v4-brew
- steps: << parameters.steps >>
- save_cache:
paths:
- /usr/local/Homebrew
- ~/Library/Caches/Homebrew
key: v4-brew
with_rntester_pods_cache_span:
parameters:
steps:
type: steps
steps:
- run:
name: Setup CocoaPods cache
# Copy packages/rn-tester/Podfile.lock since it can be changed by pod install
command: cp packages/rn-tester/Podfile.lock packages/rn-tester/Podfile.lock.bak
- restore_cache:
keys:
- v3-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}
- v3-pods-{{ .Environment.CIRCLE_JOB }}-
- steps: << parameters.steps >>
- save_cache:
paths:
- packages/rn-tester/Pods
key: v3-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}
download_gradle_dependencies:
steps:
- restore_cache:
keys:
- v1-gradle-{{ checksum "ReactAndroid/build.gradle" }}-{{ checksum "scripts/circleci/gradle_download_deps.sh" }}
- v1-gradle-
- run:
name: Download Dependencies Using Gradle
command: ./scripts/circleci/gradle_download_deps.sh
- save_cache:
paths:
- ~/.gradle
- ReactAndroid/build/downloads
- ReactAndroid/build/third-party-ndk
key: v1-gradle-{{ checksum "ReactAndroid/build.gradle" }}-{{ checksum "scripts/circleci/gradle_download_deps.sh" }}
download_buck_dependencies:
steps:
- run:
name: Download Dependencies Using Buck
command: ./scripts/circleci/buck_fetch.sh
run_e2e:
parameters:
platform:
description: Target platform
type: enum
enum: ["android", "ios", "js"]
default: "js"
retries:
description: How many times the job should try to run these tests
type: integer
default: 3
steps:
- run:
name: "Run Tests: << parameters.platform >> End-to-End Tests"
command: node ./scripts/run-ci-e2e-tests.js --<< parameters.platform >> --retries << parameters.retries >>
report_bundle_size:
parameters:
platform:
description: Target platform
type: enum
enum: ["android", "ios"]
steps:
- install_github_bot_deps
- run:
name: Report size of RNTester.app (analysis-bot)
command: GITHUB_TOKEN="$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A""$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B" scripts/circleci/report-bundle-size.sh << parameters.platform >>
# -------------------------
# JOBS
# -------------------------
jobs:
setup:
parameters:
executor:
type: executor
default: nodelts
checkout_type:
type: string
default: node
executor: << parameters.executor >>
steps:
- checkout
- save_cache:
key: v1-repo-{{ .Environment.CIRCLE_SHA1 }}-<< parameters.checkout_type >>
paths:
- ~/react-native
# -------------------------
# JOBS: Analyze PR
# -------------------------
# Analyze pull request and raise any lint/flow issues.
# Issues will be posted to the PR itself via GitHub bots.
# This workflow should only fail if the bots fail to run.
analyze_pr:
executor: nodelts
steps:
- restore_cache_checkout:
checkout_type: node
- run_yarn
- install_github_bot_deps
- run:
name: Install additional GitHub bot dependencies
command: sudo apt update && sudo apt install -y shellcheck jq
- run:
name: Run linters against modified files (analysis-bot)
command: GITHUB_TOKEN="$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A""$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B" yarn lint-ci
when: always
- run:
name: Analyze Pull Request (pull-bot)
command: |
cd bots
DANGER_GITHUB_API_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" yarn danger ci --use-github-checks
when: always
# -------------------------
# JOBS: Analyze Code
# -------------------------
analyze_code:
executor: nodelts
steps:
- restore_cache_checkout:
checkout_type: node
- setup_artifacts
- run_yarn
- run:
name: Lint code
command: scripts/circleci/exec_swallow_error.sh yarn lint --format junit -o ./reports/junit/eslint/results.xml
when: always
- run:
name: Check for errors in code using Flow (iOS)
command: yarn flow-check-ios
when: always
- run:
name: Check for errors in code using Flow (Android)
command: yarn flow-check-android
when: always
- run:
name: Sanity checks
command: |
./scripts/circleci/check_license.sh
./scripts/circleci/validate_yarn_lockfile.sh
when: always
- run:
name: Check formatting
command: yarn run format-check
when: always
- store_test_results:
path: ./reports/junit
# -------------------------
# JOBS: Test JavaScript
# -------------------------
test_js:
parameters:
executor:
type: executor
default: nodelts
run_disabled_tests:
type: boolean
default: false
executor: << parameters.executor >>
steps:
- restore_cache_checkout:
checkout_type: node
- setup_artifacts
- run_yarn
- run:
name: Install rsync
command: sudo apt-get install rsync
# -------------------------
# Run JavaScript tests
- run:
name: "Run Tests: JavaScript Tests"
command: node ./scripts/run-ci-javascript-tests.js --maxWorkers 2
- run_e2e:
platform: js
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
# -------------------------
- store_test_results:
path: ./reports/junit
# -------------------------
# JOBS: Test iOS
# -------------------------
test_ios:
executor: reactnativeios
parameters:
use_frameworks:
type: boolean
default: false
use_hermes:
type: boolean
default: false
run_unit_tests:
description: Specifies whether unit tests should run.
type: boolean
default: false
run_detox_tests:
description: Specifies whether Detox e2e tests should run.
type: boolean
default: false
run_disabled_tests:
description: Specifies whether disabled tests should run. Set this to true to debug failing tests.
type: boolean
default: false
environment:
- REPORTS_DIR: "./reports/junit"
steps:
- restore_cache_checkout:
checkout_type: ios
- setup_artifacts
- run_yarn
- run: |
cd packages/rn-tester
bundle check || bundle install
- run:
name: Boot iPhone Simulator
command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true
- run:
name: Configure Environment Variables
command: |
echo 'export PATH=/usr/local/opt/node@12/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
- with_brew_cache_span:
steps:
- brew_install:
package: watchman
- brew_install:
package: node@12
- run:
name: "Brew: Tap wix/brew"
command: HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null
- brew_install:
package: applesimutils
- run:
name: Configure Watchman
command: touch .watchmanconfig
- when:
condition: << parameters.use_frameworks >>
steps:
- run:
name: Set USE_FRAMEWORKS=1
command: echo "export USE_FRAMEWORKS=1" >> $BASH_ENV
- when:
condition: << parameters.use_hermes >>
steps:
- run:
name: Set USE_HERMES=1
command: echo "export USE_HERMES=1" >> $BASH_ENV
- run:
name: Setup the CocoaPods environment
command: pod setup
- with_rntester_pods_cache_span:
steps:
- run:
name: Generate RNTesterPods Workspace
command: cd packages/rn-tester && USE_FABRIC=1 bundle exec pod install --verbose
# -------------------------
# Runs iOS unit tests
- when:
condition: << parameters.run_unit_tests >>
steps:
- run:
name: "Run Tests: iOS Unit and Integration Tests"
command: yarn test-ios
# Runs iOS Detox e2e tests
- when:
condition: << parameters.run_detox_tests >>
steps:
- run:
name: "Run Tests: Detox iOS End-to-End Tests"
command: yarn run build-ios-e2e && yarn run test-ios-e2e
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
- run:
name: "Run Tests: CocoaPods"
command: ./scripts/process-podspecs.sh
- run:
name: Free up port 8081 for iOS End-to-End Tests
command: |
# free up port 8081 for the packager before running tests
set +eo pipefail
lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
set -eo pipefail
- run_e2e:
platform: ios
# -------------------------
# Collect Results
- report_bundle_size:
platform: ios
- store_test_results:
path: ./reports/junit
# -------------------------
# JOBS: Test Android
# -------------------------
test_android:
executor: reactnativeandroid
parameters:
run_disabled_tests:
type: boolean
default: false
steps:
- restore_cache_checkout:
checkout_type: android
- setup_artifacts
- run_yarn
# Validate Android SDK installation and packages
- run:
name: Validate Android SDK Install
command: ./scripts/validate-android-sdk.sh
# Starting emulator in advance as it takes some time to boot.
- run:
name: Create Android Virtual Device
command: source scripts/android-setup.sh && createAVD
- run:
name: Launch Android Virtual Device in Background
command: source scripts/android-setup.sh && launchAVD
background: true
# Keep configuring Android dependencies while AVD boots up
- run:
name: Install rsync, zip
command: apt-get update -y && apt-get install rsync zip -y
# Install Buck
- install_buck_tooling
# Validate Android test environment (including Buck)
- run:
name: Validate Android Test Environment
command: ./scripts/validate-android-test-env.sh
- download_buck_dependencies
- download_gradle_dependencies
# Build and compile
- run:
name: Build Android App
command: |
buck build ReactAndroid/src/main/java/com/facebook/react
buck build ReactAndroid/src/main/java/com/facebook/react/shell
- run:
name: Compile Native Libs for Unit and Integration Tests
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS
no_output_timeout: 6m
# Build JavaScript Bundle for instrumentation tests
- run:
name: Build JavaScript Bundle
command: node cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
# Wait for AVD to finish booting before running tests
- run:
name: Wait for Android Virtual Device
command: source scripts/android-setup.sh && waitForAVD
- run:
name: Assemble RNTester App
command: ./gradlew packages:rn-tester:android:app:assembleRelease
# -------------------------
# Run Android tests
- run:
name: "Run Tests: Android Unit Tests"
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS --xml ./reports/buck/all-results-raw.xml
- run:
name: "Build Tests: Android Instrumentation Tests"
# Here, just build the instrumentation tests. There is a known issue with installing the APK to android-21+ emulator.
command: |
if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
echo "JavaScript bundle missing, cannot run instrumentation tests. Verify Build JavaScript Bundle step completed successfully."; exit 1;
fi
source scripts/android-setup.sh && NO_BUCKD=1 retry3 timeout 300 buck build ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
- run_e2e:
platform: android
# -------------------------
# Collect Results
- report_bundle_size:
platform: android
- run:
name: Collect Test Results
command: |
find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ./reports/build/ \;
find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ./reports/outputs/ \;
find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ./reports/buck/ \;
if [ -f ~/react-native/reports/buck/all-results-raw.xml ]; then
cd ~/okbuck
./tooling/junit/buck_to_junit.sh ~/react-native/reports/buck/all-results-raw.xml ~/react-native/reports/junit/results.xml
fi
when: always
- store_test_results:
path: ./reports/junit
# -------------------------
# JOBS: Test Docker
# -------------------------
test_docker:
machine: true
steps:
- checkout
- run:
name: Build Docker container for Android RNTester App
command: |
source ~/.bashrc
nvm i node
npm i -g yarn
echo y | npx envinfo@latest
yarn run docker-setup-android
yarn run docker-build-android
# -------------------------
# JOBS: Windows
# -------------------------
test_windows:
executor:
name: win/default
parameters:
run_disabled_tests:
type: boolean
default: false
environment:
- ANDROID_HOME: "C:\\Android\\android-sdk"
- ANDROID_NDK: "C:\\Android\\android-sdk\\ndk\\19.2.5345600"
- ANDROID_BUILD_VERSION: 28
- ANDROID_TOOLS_VERSION: 29.0.3
- GRADLE_OPTS: -Dorg.gradle.daemon=false
- NDK_VERSION: 19.2.5345600
steps:
- checkout
# Setup Dependencies
- run:
name: Install Yarn
command: choco install yarn
- run:
name: Display Environment info
command: npx envinfo@latest
- restore_cache:
keys:
- v1-win-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
- v1-win-yarn-cache-{{ arch }}-
- run:
name: "Yarn: Install Dependencies"
command: yarn install --frozen-lockfile --non-interactive
- save_cache:
key: v1-win-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
paths:
- C:\Users\circleci\AppData\Local\Yarn
- run:
name: Install Android SDK Tools
command: choco install android-sdk
- run:
name: Setup Android SDKs
command: |
sdkmanager --licenses
sdkmanager "system-images;android-21;google_apis;armeabi-v7a"
sdkmanager "platforms;android-%ANDROID_BUILD_VERSION%"
sdkmanager "build-tools;%ANDROID_TOOLS_VERSION%"
sdkmanager "add-ons;addon-google_apis-google-23"
sdkmanager "extras;android;m2repository"
sdkmanager "ndk;%NDK_VERSION%"
# -------------------------
# Run Tests
- run:
name: "Flow: Check Android"
command: yarn flow-check-android
- run:
name: "Flow: Check iOS"
command: yarn flow-check-ios
- run:
name: "Run Tests: JavaScript Tests"
command: yarn test
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
- run:
name: Android Build
command: ./gradlew.bat packages:rn-tester:android:app:assembleRelease
# -------------------------
# JOBS: Coverage
# -------------------------
# Collect JavaScript test coverage
js_coverage:
executor: nodelts
environment:
- CI_BRANCH: $CIRCLE_BRANCH
- CI_PULL_REQUEST: $CIRCLE_PULL_REQUEST
- CI_BUILD_NUMBER: $CIRCLE_BUILD_NUM
- CI_BUILD_URL: $CIRCLE_BUILD_URL
steps:
- restore_cache_checkout:
checkout_type: node
- setup_artifacts
- run_yarn
- run:
name: Collect test coverage information
command: |
scripts/circleci/exec_swallow_error.sh yarn test --coverage --maxWorkers=2
if [[ -e ./coverage/lcov.info ]]; then
cat ./coverage/lcov.info | scripts/circleci/exec_swallow_error.sh ./node_modules/.bin/coveralls
fi
- store_artifacts:
path: ~/react-native/coverage/
# -------------------------
# JOBS: Releases
# -------------------------
# Publishes a new version onto npm
publish_npm_package:
parameters:
publish_npm_args:
type: string
default: --nonightly
executor: reactnativeandroid
steps:
- run:
name: Add github.com to SSH known hosts
command: |
mkdir -p ~/.ssh
echo '|1|If6MU203eXTaaWL678YEfWkVMrw=|kqLeIAyTy8pzpj8x8Ae4Fr8Mtlc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
- restore_cache_checkout:
checkout_type: android
- run_yarn
- install_buck_tooling
- download_buck_dependencies
- download_gradle_dependencies
- run: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
- run: |
git config --global user.email "react-native-bot@users.noreply.github.com"
git config --global user.name "npm Deployment Script"
echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc
- run: node ./scripts/publish-npm.js << parameters.publish_npm_args >>
# -------------------------
# JOBS: Nightly
# -------------------------
nightly_job:
machine: true
steps:
- run:
name: Nightly
command: |
echo "Nightly build run"
# -------------------------
# WORK FLOWS
# -------------------------
workflows:
version: 2
tests:
jobs:
- setup:
name: setup_js
filters:
branches:
ignore: gh-pages
- setup:
name: setup_ios
checkout_type: ios
executor: reactnativeios
filters:
branches:
ignore: gh-pages
- setup:
name: setup_android
checkout_type: android
executor: reactnativeandroid
filters:
branches:
ignore: gh-pages
- test_js:
run_disabled_tests: false
requires:
- setup_js
- test_android:
run_disabled_tests: false
requires:
- setup_android
- test_ios:
name: test_ios_unit_jsc
run_unit_tests: true
requires:
- setup_ios
- test_ios:
name: test_ios_unit_frameworks_jsc
use_frameworks: true
run_unit_tests: true
requires:
- setup_ios
- test_ios:
name: test_ios_unit_hermes
use_hermes: true
run_unit_tests: true
requires:
- setup_ios
# Hermes doesn't support dynamic frameworks right now.
# The following configuration will fail.
# - test_ios:
# name: test_ios_unit_frameworks_hermes
# use_hermes: true
# use_frameworks: true
# run_unit_tests: true
# requires:
# - setup_ios
# - test_ios:
# name: test_ios_detox
# run_detox_tests: true
# requires:
# - setup_ios
# - test_ios:
# name: test_ios_detox_frameworks
# use_frameworks: true
# run_detox_tests: true
# requires:
# - setup_ios
- test_js:
name: test_js_prev_lts
executor: nodeprevlts
requires:
- setup_js
- test_docker:
filters:
branches:
ignore: gh-pages
- test_windows:
filters:
branches:
ignore: gh-pages
run_disabled_tests: false
releases:
jobs:
- setup:
name: setup_android
checkout_type: android
executor: reactnativeandroid
filters:
# Both of the following conditions must be included!
# Ignore any commit on any branch by default.
branches:
ignore: /.*/
# Only act on version tags.
tags:
only: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/
- publish_npm_package:
requires:
- setup_android
filters:
# Both of the following conditions must be included!
# Ignore any commit on any branch by default.
branches:
ignore: /.*/
# Only act on version tags.
tags:
only: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/
analysis:
jobs:
- setup
# Run lints on every commit other than those to the gh-pages branch
- analyze_code:
requires:
- setup
filters:
branches:
ignore: gh-pages
# Run code checks on PRs from forks
- analyze_pr:
requires:
- setup
filters:
branches:
only: /^pull\/.*$/
# Gather coverage
- js_coverage:
requires:
- setup
nightly:
triggers:
- schedule:
cron: "0 0 * * *"
filters:
branches:
only:
- master
jobs:
- nightly_job
- setup:
name: setup_android
checkout_type: android
executor: reactnativeandroid
- publish_npm_package:
publish_npm_args: --nightly
requires:
- setup_android
-91
View File
@@ -1,91 +0,0 @@
---
AccessModifierOffset: -1
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ]
IncludeCategories:
- Regex: '^<.*\.h(pp)?>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
---
Language: ObjC
ColumnLimit: 120
BreakBeforeBraces: WebKit
...
+1 -8
View File
@@ -1,4 +1,4 @@
# EditorConfig is awesome: https://EditorConfig.org
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
@@ -12,10 +12,3 @@ indent_size = 2
[*.gradle]
indent_size = 4
[BUCK]
indent_size = 4
# Windows files
[*.bat]
end_of_line = crlf
+13 -8
View File
@@ -1,10 +1,15 @@
**/main.js
# node_modules ignored by default
**/staticBundle.js
bots/node_modules
docs/generatedComponentApiDocs.js
flow/
Libraries/Renderer/*
**/main.js
Libraries/vendor/**/*
node_modules/
packages/*/node_modules
packages/react-native-codegen/lib
Libraries/Renderer/*
website/node_modules
pr-inactivity-bookmarklet.js
question-bookmarklet.js
flow/
website/core/metadata.js
website/core/metadata-blog.js
website/src/react-native/docs/
website/src/react-native/blog/
danger/
+241 -44
View File
@@ -1,52 +1,249 @@
{
"root": true,
"extends": [
"./packages/eslint-config-react-native-community/index.js"
],
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"env": {
"es6": true,
"jasmine": true,
},
"plugins": [
"@react-native/eslint-plugin-codegen"
"flowtype",
"prettier",
"react"
],
"overrides": [
{
"files": [
"Libraries/**/*.js",
],
"rules": {
"@react-native-community/no-haste-imports": 2,
"@react-native-community/error-subclass-name": 2,
"@react-native-community/platform-colors": 2,
"@react-native/codegen/react-native-modules": 2
}
},
{
"files": [
"**/__fixtures__/**/*.js",
"**/__mocks__/**/*.js",
"**/__tests__/**/*.js",
"jest/**/*.js",
"packages/rn-tester/**/*.js",
],
"globals": {
// Expose some Jest globals for test helpers
"afterAll": true,
"afterEach": true,
"beforeAll": true,
"beforeEach": true,
"expect": true,
"jest": true,
},
},
{
"files": [
"**/__tests__/**/*-test.js",
],
"env": {
"jasmine": true,
"jest": true
}
}
]
// Map from global var to bool specifying if it can be redefined
"globals": {
"__DEV__": true,
"__dirname": false,
"__fbBatchedBridgeConfig": false,
"alert": false,
"cancelAnimationFrame": false,
"cancelIdleCallback": false,
"clearImmediate": true,
"clearInterval": false,
"clearTimeout": false,
"console": false,
"document": false,
"escape": false,
"Event": false,
"EventTarget": false,
"exports": false,
"fetch": false,
"FormData": false,
"global": false,
"jest": false,
"Map": true,
"module": false,
"navigator": false,
"process": false,
"Promise": true,
"requestAnimationFrame": true,
"requestIdleCallback": true,
"require": false,
"Set": true,
"setImmediate": true,
"setInterval": false,
"setTimeout": false,
"window": false,
"XMLHttpRequest": false,
"pit": false
},
"rules": {
// Flow Plugin
// The following rules are made available via `eslint-plugin-flowtype`
"flowtype/define-flow-type": 1,
"flowtype/use-flow-type": 1,
// General
// This must be disallowed in this repo because the minimum supported
// version of node is 4 which doesn't support trailing commas.
// Once the minimum supported version is 8 or greater this can be changed
"comma-dangle": [2, { // disallow trailing commas in object literals
"arrays": "ignore",
"objects": "ignore",
"imports": "ignore",
"exports": "ignore",
"functions": "never"
}],
"no-cond-assign": 1, // disallow assignment in conditional expressions
"no-console": 0, // disallow use of console (off by default in the node environment)
"no-const-assign": 2, // disallow assignment to const-declared variables
"no-constant-condition": 0, // disallow use of constant expressions in conditions
"no-control-regex": 1, // disallow control characters in regular expressions
"no-debugger": 1, // disallow use of debugger
"no-dupe-keys": 1, // disallow duplicate keys when creating object literals
"no-empty": 0, // disallow empty statements
"no-ex-assign": 1, // disallow assigning to the exception in a catch block
"no-extra-boolean-cast": 1, // disallow double-negation boolean casts in a boolean context
"no-extra-parens": 0, // disallow unnecessary parentheses (off by default)
"no-extra-semi": 1, // disallow unnecessary semicolons
"no-func-assign": 1, // disallow overwriting functions written as function declarations
"no-inner-declarations": 0, // disallow function or variable declarations in nested blocks
"no-invalid-regexp": 1, // disallow invalid regular expression strings in the RegExp constructor
"no-negated-in-lhs": 1, // disallow negation of the left operand of an in expression
"no-obj-calls": 1, // disallow the use of object properties of the global object (Math and JSON) as functions
"no-regex-spaces": 1, // disallow multiple spaces in a regular expression literal
"no-reserved-keys": 0, // disallow reserved words being used as object literal keys (off by default)
"no-sparse-arrays": 1, // disallow sparse arrays
"no-unreachable": 1, // disallow unreachable statements after a return, throw, continue, or break statement
"use-isnan": 1, // disallow comparisons with the value NaN
"valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default)
"valid-typeof": 1, // Ensure that the results of typeof are compared against a valid string
// Best Practices
// These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns.
"block-scoped-var": 0, // treat var statements as if they were block scoped (off by default)
"complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default)
"consistent-return": 0, // require return statements to either always or never specify values
"curly": 1, // specify curly brace conventions for all control statements
"default-case": 0, // require default case in switch statements (off by default)
"dot-notation": 1, // encourages use of dot notation whenever possible
"eqeqeq": [1, "allow-null"], // require the use of === and !==
"guard-for-in": 0, // make sure for-in loops have an if statement (off by default)
"no-alert": 1, // disallow the use of alert, confirm, and prompt
"no-caller": 1, // disallow use of arguments.caller or arguments.callee
"no-div-regex": 1, // disallow division operators explicitly at beginning of regular expression (off by default)
"no-else-return": 0, // disallow else after a return in an if (off by default)
"no-eq-null": 0, // disallow comparisons to null without a type-checking operator (off by default)
"no-eval": 1, // disallow use of eval()
"no-extend-native": 1, // disallow adding to native types
"no-extra-bind": 1, // disallow unnecessary function binding
"no-fallthrough": 1, // disallow fallthrough of case statements
"no-floating-decimal": 1, // disallow the use of leading or trailing decimal points in numeric literals (off by default)
"no-implied-eval": 1, // disallow use of eval()-like methods
"no-labels": 1, // disallow use of labeled statements
"no-iterator": 1, // disallow usage of __iterator__ property
"no-lone-blocks": 1, // disallow unnecessary nested blocks
"no-loop-func": 0, // disallow creation of functions within loops
"no-multi-str": 0, // disallow use of multiline strings
"no-native-reassign": 0, // disallow reassignments of native objects
"no-new": 1, // disallow use of new operator when not part of the assignment or comparison
"no-new-func": 1, // disallow use of new operator for Function object
"no-new-wrappers": 1, // disallows creating new instances of String,Number, and Boolean
"no-octal": 1, // disallow use of octal literals
"no-octal-escape": 1, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
"no-proto": 1, // disallow usage of __proto__ property
"no-redeclare": 0, // disallow declaring the same variable more then once
"no-return-assign": 1, // disallow use of assignment in return statement
"no-script-url": 1, // disallow use of javascript: urls.
"no-self-compare": 1, // disallow comparisons where both sides are exactly the same (off by default)
"no-sequences": 1, // disallow use of comma operator
"no-unused-expressions": 0, // disallow usage of expressions in statement position
"no-void": 1, // disallow use of void operator (off by default)
"no-warning-comments": 0, // disallow usage of configurable warning terms in comments": 1, // e.g. TODO or FIXME (off by default)
"no-with": 1, // disallow use of the with statement
"radix": 1, // require use of the second argument for parseInt() (off by default)
"semi-spacing": 1, // require a space after a semi-colon
"vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default)
"wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default)
"yoda": 1, // require or disallow Yoda conditions
// Variables
// These rules have to do with variable declarations.
"no-catch-shadow": 1, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment)
"no-delete-var": 1, // disallow deletion of variables
"no-label-var": 1, // disallow labels that share a name with a variable
"no-shadow": 1, // disallow declaration of variables already declared in the outer scope
"no-shadow-restricted-names": 1, // disallow shadowing of names such as arguments
"no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block
"no-undefined": 0, // disallow use of undefined variable (off by default)
"no-undef-init": 1, // disallow use of undefined when initializing variables
"no-unused-vars": [1, {"vars": "all", "args": "none"}], // disallow declaration of variables that are not used in the code
"no-use-before-define": 0, // disallow use of variables before they are defined
// Node.js
// These rules are specific to JavaScript running on Node.js.
"handle-callback-err": 1, // enforces error handling in callbacks (off by default) (on by default in the node environment)
"no-mixed-requires": 1, // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment)
"no-new-require": 1, // disallow use of new operator with the require function (off by default) (on by default in the node environment)
"no-path-concat": 1, // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment)
"no-process-exit": 0, // disallow process.exit() (on by default in the node environment)
"no-restricted-modules": 1, // restrict usage of specified node modules (off by default)
"no-sync": 0, // disallow use of synchronous methods (off by default)
// Prettier Plugin
// https://github.com/prettier/eslint-plugin-prettier
"prettier/prettier": [2, "fb", "@format"],
// Stylistic Issues
// These rules are purely matters of style and are quite subjective.
"key-spacing": 0,
"keyword-spacing": 1, // enforce spacing before and after keywords
"jsx-quotes": [1, "prefer-double"],
"comma-spacing": 0,
"no-multi-spaces": 0,
"brace-style": 0, // enforce one true brace style (off by default)
"camelcase": 0, // require camel case names
"consistent-this": [1, "self"], // enforces consistent naming when capturing the current execution context (off by default)
"eol-last": 1, // enforce newline at the end of file, with no multiple empty lines
"func-names": 0, // require function expressions to have a name (off by default)
"func-style": 0, // enforces use of function declarations or expressions (off by default)
"new-cap": 0, // require a capital letter for constructors
"new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments
"no-nested-ternary": 0, // disallow nested ternary expressions (off by default)
"no-array-constructor": 1, // disallow use of the Array constructor
"no-lonely-if": 0, // disallow if as the only statement in an else block (off by default)
"no-new-object": 1, // disallow use of the Object constructor
"no-spaced-func": 1, // disallow space between function identifier and application
"no-ternary": 0, // disallow the use of ternary operators (off by default)
"no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines
"no-underscore-dangle": 0, // disallow dangling underscores in identifiers
"no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation
"quotes": [1, "single", "avoid-escape"], // specify whether double or single quotes should be used
"quote-props": 0, // require quotes around object literal property names (off by default)
"semi": 1, // require or disallow use of semicolons instead of ASI
"sort-vars": 0, // sort variables within the same declaration block (off by default)
"space-in-brackets": 0, // require or disallow spaces inside brackets (off by default)
"space-in-parens": 0, // require or disallow spaces inside parentheses (off by default)
"space-infix-ops": 1, // require spaces around operators
"space-unary-ops": [1, { "words": true, "nonwords": false }], // require or disallow spaces before/after unary operators (words on by default, nonwords off by default)
"max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default)
"one-var": 0, // allow just one var statement per function (off by default)
"wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default)
// Legacy
// The following rules are included for compatibility with JSHint and JSLint. While the names of the rules may not match up with the JSHint/JSLint counterpart, the functionality is the same.
"max-depth": 0, // specify the maximum depth that blocks can be nested (off by default)
"max-len": 0, // specify the maximum length of a line in your program (off by default)
"max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default)
"max-statements": 0, // specify the maximum number of statement allowed in a function (off by default)
"no-bitwise": 1, // disallow use of bitwise operators (off by default)
"no-plusplus": 0, // disallow use of unary operators, ++ and -- (off by default)
// React Plugin
// The following rules are made available via `eslint-plugin-react`.
"react/display-name": 0,
"react/jsx-boolean-value": 0,
"react/jsx-no-comment-textnodes": 1,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 1,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-did-mount-set-state": 1,
"react/no-did-update-set-state": 1,
"react/no-multi-comp": 0,
"react/no-string-refs": 1,
"react/no-unknown-property": 0,
"react/prop-types": 0,
"react/react-in-jsx-scope": 1,
"react/self-closing-comp": 1,
"react/wrap-multilines": 0
}
}
+24 -40
View File
@@ -3,71 +3,55 @@
.*/*[.]android.js
; Ignore templates for 'react-native init'
<PROJECT_ROOT>/template/.*
.*/local-cli/templates/.*
; Ignore the website subdir
<PROJECT_ROOT>/website/.*
; Ignore the Dangerfile
<PROJECT_ROOT>/bots/dangerfile.js
<PROJECT_ROOT>/danger/dangerfile.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
[untyped]
.*/node_modules/@react-native-community/cli/.*/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
[include]
[declarations]
.*/node_modules/.*
[libs]
interface.js
Libraries/react-native/react-native-interface.js
flow/
[options]
emoji=true
exact_by_default=true
module.file_ext=.js
module.file_ext=.json
module.file_ext=.ios.js
module.system=haste
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FlowFixMeEmpty
suppress_type=$FixMe
experimental.abstract_locations=true
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-3]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-3]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
unnecessary-invariant=warn
signature-verification-failure=warn
[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import
unsafe.enable_getters_and_setters=true
[version]
^0.137.0
^0.53.0
-73
View File
@@ -1,73 +0,0 @@
[ignore]
; We fork some components by platform
.*/*[.]ios.js
; Ignore templates for 'react-native init'
<PROJECT_ROOT>/template/.*
; Ignore the Dangerfile
<PROJECT_ROOT>/bots/dangerfile.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
[untyped]
.*/node_modules/@react-native-community/cli/.*/.*
[include]
[declarations]
.*/node_modules/.*
[libs]
interface.js
flow/
[options]
emoji=true
exact_by_default=true
module.file_ext=.js
module.file_ext=.json
module.file_ext=.android.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FlowFixMeEmpty
experimental.abstract_locations=true
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
unnecessary-invariant=warn
signature-verification-failure=warn
[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import
[version]
^0.137.0
+6 -3
View File
@@ -1,3 +1,6 @@
# Windows files should use crlf line endings
# https://help.github.com/articles/dealing-with-line-endings/
*.bat text eol=crlf
# Force LF line endings for Bash scripts. On Windows the rest of the source
# files will typically have CR+LF endings (Git default on Windows), but Bash
# scripts need to have LF endings to work (under Cygwin), thus override to force
# that.
gradlew text eol=lf
*.sh text eol=lf
+14 -36
View File
@@ -1,40 +1,18 @@
# See https://help.github.com/en/articles/about-code-owners
# to learn more about code owners.
# Order is important; the last matching pattern takes the most
# precedence. You may specify either a GitHub username, or an
# email address if you prefer, as the code owner.
# Any Markdown file anywhere in the repository
**/*.md @hramos @cpojer
# GitHub Settings, Bots
/.github/ @hramos
/bots @hramos
# Continuous Integration
/.circleci/ @hramos
/.circleci/Dockerfiles @gengjiawen
/.appveyor/ @gengjiawen
# Internals
React/Base/* @shergin
React/Views/* @shergin
React/Modules/* @shergin
React/CxxBridge/* @mhorowitz
# Components and APIs
ReactAndroid/src/main/java/com/facebook/react/animated/* @janicduplessis
docs/* @hramos
blog/* @hramos
Libraries/Animated/* @janicduplessis
Libraries/NativeAnimation/* @janicduplessis
Libraries/Image/* @shergin
Libraries/Text/* @shergin
# Modifications to package.json typically require
# additional effort from a Facebook employee to land
/package.json @hramos @cpojer
# These should not be modified through a GitHub PR
LICENSE* @hramos @cpojer @yungsters
# The eslint-config-react-native-community package requires manual publishing after merging
/packages/eslint-config-react-native-community/* @matt-oakes
React/Base/* @shergin
React/Views/* @shergin
React/Modules/* @shergin
React/CxxBridge/* @mhorowitz
ReactAndroid/src/main/java/com/facebook/react/animated/* @janicduplessis
website/* @hramos
website/showcase.json @hramos
package.json @hramos
website/package.json @hramos
local-cli/core/* @grabbou @kureev
local-cli/link/* @grabbou @kureev
local-cli/unlink/* @grabbou @kureev
+117 -3
View File
@@ -1,4 +1,118 @@
✋ To keep the backlog clean and actionable, issues will be
🚫 closed if they do not follow one of the issue templates:
👉 https://github.com/facebook/react-native/issues/new/choose
<!--
PLEASE DON'T DELETE THIS TEMPLATE UNTIL YOU HAVE READ THE FIRST SECTION.
-->
### Is this a bug report?
(write your answer here)
<!--
If you answered "Yes":
We expect that it will take you about 30 minutes to produce a high-quality bug report.
While this may seem like a lot, putting care into issues helps us fix them faster.
For bug reports, it is REQUIRED to fill the rest of this template, or the issue will be closed.
If you answered "No":
We use GitHub Issues exclusively for tracking bugs in React Native. If you're looking for help,
the Community page at http://facebook.github.io/react-native/support.html list various resources
that should help you get started.
Now scroll below!
-->
### Have you read the [Contributing Guidelines](https://facebook.github.io/react-native/docs/contributing.html)?
(Write your answer here.)
### Environment
<!--
Please fill in all the relevant fields by running these commands in terminal.
-->
1. `react-native -v`:
2. `node -v`:
3. `npm -v`:
4. `yarn --version`<!-- (if you use Yarn) -->:
Then, specify:
<!-- (What platform are you building for? Choose any from iOS, Android, AppleTV.) -->
- Target Platform:
<!-- Which operating system are you using? Specify macOS, Windows, or Linux, along with specific release versions -->
- Development Operating System:
<!-- Include any additional relevant information. Are you using Xcode or Android Studio to build native code? Is the issue specific to a particular iOS or Android SDK? -->
- Build tools:
### Steps to Reproduce
<!--
How would you describe your issue to someone who doesnt know you or your project?
Try to write a sequence of steps that anybody can repeat to see the issue.
Be specific! If the bug cannot be reproduced, your issue may be closed.
-->
(Write your steps here:)
1.
2.
3.
### Expected Behavior
<!--
How did you expect your project to behave?
Its fine if youre not sure your understanding is correct.
Just write down what you thought would happen.
-->
(Write what you thought would happen.)
### Actual Behavior
<!--
Did something go wrong?
Is something broken, or not behaving as you expected?
Describe this section in detail, and attach screenshots if possible.
Don't just say "it doesn't work"!
-->
(Write what happened. Add screenshots!)
### Reproducible Demo
<!--
Please share a project that reproduces the issue.
There are two ways to do it:
* Create a new app using https://snack.expo.io/ and try to reproduce the issue in it.
This is useful if you roughly know where the problem is, or cant share the real code.
* Or, copy your app and remove things until youre left with the minimal reproducible demo.
This is useful for finding the root cause. You may then optionally create a Snack.
This is a good guide to creating bug demos: https://stackoverflow.com/help/mcve
Once youre done, copy and paste the link to the Snack or a public GitHub repository below:
-->
(Paste the link to an example project and exact instructions to reproduce the issue.)
<!--
What happens if you skip this step?
Someone will read your bug report, and maybe will be able to help you,
but its unlikely that it will get much attention from the team. Eventually,
the issue will likely get closed in favor of issues that have reproducible demos.
Please remember that:
* Issues without reproducible demos have a very low priority.
* The person fixing the bug would have to do that anyway. Please be respectful of their time.
* You might figure out the issues yourself as you work on extracting it.
Thanks for helping us help you!
-->
-30
View File
@@ -1,30 +0,0 @@
---
name: "🐛 Bug Report"
about: Report a reproducible bug or regression in React Native.
title: ''
labels: 'Needs: Triage :mag:'
---
Please provide all the information requested. Issues that do not follow this format are likely to stall.
## Description
Please provide a clear and concise description of what the bug is. Include screenshots if needed.
Please test using the latest React Native release to make sure your issue has not already been fixed: https://reactnative.dev/docs/upgrading.html
## React Native version:
Run `react-native info` in your terminal and copy the results here.
## Steps To Reproduce
Provide a detailed list of steps that reproduce the issue.
1.
2.
## Expected Results
Describe what you expected to happen.
## Snack, code example, screenshot, or link to a repository:
Please provide a Snack (https://snack.expo.io/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
You may provide a screenshot of the application if you think it is relevant to your bug report.
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
-14
View File
@@ -1,14 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: 📃 Documentation Issue
url: https://github.com/facebook/react-native-website/issues
about: Please report documentation issues in the React Native website repository.
- name: ⤴️ Upgrade Issue
url: https://github.com/react-native-community/upgrade-support
about: Need help upgrading to a newer React Native version? Visit the Upgrade Support repository.
- name: 🤔 Questions and Help
url: https://reactnative.dev/help
about: Looking for help with your app? Please refer to the React Native community's support resources.
- name: 🚀 Discussions and Proposals
url: https://github.com/react-native-community/discussions-and-proposals
about: Discuss the future of React Native in the React Native community's discussions and proposals repository.
+7 -8
View File
@@ -1,17 +1,16 @@
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. -->
<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.
## Summary
Help us understand your motivation by explaining why you decided to make this change.
<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->
You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html
## Changelog
Happy contributing!
<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->
[CATEGORY] [TYPE] - Message
(Write your motivation here.)
## Test Plan
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!)
-35
View File
@@ -1,35 +0,0 @@
Thanks for using React Native! If you need help with your React Native app, the right place to go depends on the type of help that you need.
## 🤔 I have a question or need help with my React Native app.
If you have a coding question related to React Native, it might be better suited for Stack Overflow. It's a great place to browse through [frequent questions about using React Native](https://stackoverflow.com/questions/tagged/react-native?sort=frequent&pageSize=15), as well as [ask for help with specific questions](https://stackoverflow.com/questions/tagged/react-native).
[Reactiflux](https://www.reactiflux.com/) is an active community of React and React Native developers. If you are looking for immediate assistance or have a general question about React Native, the #react-native channel is a good place to start.
## 📃 I found something that seems wrong in the documentation.
The React Native website is hosted on a [separate repository](https://github.com/facebook/react-native-website). If you want to report something that is wrong or missing from the documentation, [please open a new issue there](https://github.com/facebook/react-native-website/issues).
## 🐛 I found a bug in React Native.
If you want to report a reproducible bug or regression in the React Native library, you can [create a new issue](https://github.com/facebook/react-native/issues/new?labels=Type%3A+Bug+Report&template=bug_report.md). It's a good idea to look through [open issues](https://github.com/facebook/react-native/issues) before doing so, as someone else may have reported a similar issue.
## 🚀 I want to discuss the future of React Native.
If you'd like to discuss topics related to the future of React Native, please check out the [React Native Community Discussions and Proposals](https://github.com/react-native-community/discussions-and-proposals) repository.
## 💬 I want to talk to other React Native developers.
If you want to participate in casual discussions about the use of React Native, consider participating in one of the following forums:
- [Reactiflux Discord Server](https://www.reactiflux)
- [Spectrum Chat](https://spectrum.chat/react-native)
- [React Native Community Facebook Group](https://www.facebook.com/groups/react.native.community)
> For a full list of community resources, check out [React Native's Community page](https://reactnative.dev/help).
-56
View File
@@ -1,56 +0,0 @@
# Configuration for Label Actions - https://github.com/marketplace/actions/label-actions
"Type: Invalid":
close: true
"Type: Question":
comment: >
We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/).
close: true
"Type: Docs":
comment: >
Please report documentation issues in the [`react-native-website`](https://github.com/facebook/react-native-website/issues) repository.
close: true
"Type: Upgrade Issue":
comment: >
Do you need help upgrading to a newer React Native version? Visit the [Upgrade Support repository](https://github.com/react-native-community/upgrade-support) or use the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) to see the changes that need to be made to upgrade your app.
close: true
"Resolution: For Stack Overflow":
comment: >
We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question.
close: true
"Needs: Issue Template":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Required Fields
</th></tr><tr><td>:information_source:</td><td>
It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a <a href="https://github.com/facebook/react-native/issues/new?template=bug_report.md">new issue is created</a>. Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Environment Info":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Environment Information
</th></tr><tr><td>:information_source:</td><td>
Your issue may be missing information about your development environment. You can obtain the missing information by running <code>react-native info</code> in a console.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Verify on Latest Version":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Using Old Version
</th></tr><tr><td>:information_source:</td><td>
It looks like you are using an older version of React Native. Please <a href="https://reactnative.dev/docs/upgrading">upgrade</a> to the latest version, and verify if the issue persists. If it does not, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the current release.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Repro":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Reproducible Example
</th></tr><tr><td>:information_source:</td><td>
It looks like your issue is missing a reproducible example. Please provide a <a href="https://snack.expo.io">Snack</a> or a repository that demonstrates the issue you are reporting in a <a href="https://stackoverflow.com/help/minimal-reproducible-example">minimal, complete, and reproducible</a> manner.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
-26
View File
@@ -1,26 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- Good first issue
- "Type: Discussion"
- Partner
- Core Team
- "Help Wanted :octocat:"
- "Impact: Regression"
- "Resolution: PR Submitted"
- "Resolution: Backlog"
# Label to use when marking an issue as stale
staleLabel: Stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs.
You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
only: issues
-21
View File
@@ -1,21 +0,0 @@
name: Issue Needs Attention
# This workflow is triggered on issue comments.
on:
issue_comment:
types: created
jobs:
applyNeedsAttentionLabel:
name: Apply Needs Attention Label
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Apply Needs Attention Label
uses: hramos/needs-attention@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
response-required-label: "Needs: Author Feedback"
needs-attention-label: "Needs: Attention"
id: needs-attention
- name: Result
run: echo '${{ steps.needs-attention.outputs.result }}'
@@ -1,16 +0,0 @@
name: Label Actions
# This workflow is triggered when a label is added to an issue.
on:
issues:
types: labeled
jobs:
processLabelAction:
name: Process Label Action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Process Label Action
uses: hramos/label-actions@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
+7 -58
View File
@@ -23,34 +23,19 @@ project.xcworkspace
# Gradle
/build/
/packages/react-native-codegen/android/build/
/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build
/packages/rn-tester/android/app/.cxx/
/packages/rn-tester/android/app/build/
/packages/rn-tester/android/app/gradle/
/packages/rn-tester/android/app/gradlew
/packages/rn-tester/android/app/gradlew.bat
/RNTester/android/app/build/
/RNTester/android/app/gradle/
/RNTester/android/app/gradlew
/RNTester/android/app/gradlew.bat
/ReactAndroid/build/
/ReactAndroid/gradle/
/ReactAndroid/gradlew
/ReactAndroid/gradlew.bat
# Buck
.buckd
buck-out
/.lsp.buckd
/.lsp-buck-out
/ReactAndroid/src/main/jni/prebuilt/lib/
/ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/
/ReactAndroid/src/main/jni/prebuilt/lib/x86/
/ReactAndroid/src/main/gen
# Android Studio
.project
.settings
.classpath
# Watchman
.watchmanconfig
# Android
.idea
.gradle
@@ -62,8 +47,7 @@ local.properties
node_modules
*.log
.nvm
/bots/node_modules/
package-lock.json
/danger/node_modules/
# OS X
.DS_Store
@@ -74,38 +58,3 @@ package-lock.json
/coverage
/third-party
# Root dir shouldn't have Xcode project
/*.xcodeproj
# ReactCommon subdir shouldn't have Xcode project
/ReactCommon/**/*.xcodeproj
/packages/rn-tester/build
/packages/rn-tester/android/app/build/*
# Libs that shouldn't have Xcode project
/Libraries/FBLazyVector/**/*.xcodeproj
/Libraries/RCTRequired/**/*.xcodeproj
/React/CoreModules/**/*.xcodeproj
/React/FBReactNativeSpec/**/*.xcodeproj
/packages/react-native-codegen/**/*.xcodeproj
# CocoaPods
/template/ios/Pods/
/template/ios/Podfile.lock
/packages/rn-tester/Gemfile.lock
# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
/packages/rn-tester/Pods/*
!/packages/rn-tester/Pods/__offline_mirrors__
# react-native-codegen
/packages/react-native-codegen/lib
/ReactCommon/react/renderer/components/rncore/
# Visual studio
.vscode
.vs
# Android memory profiler files
*.hprof
+3
View File
@@ -0,0 +1,3 @@
# rnpm
/local-cli/rnpm
/local-cli/server/middleware/heapCapture/bundle.js
-1
View File
@@ -1 +0,0 @@
8.11.3
-8
View File
@@ -1,8 +0,0 @@
{
"requirePragma": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"jsxBracketSameLine": true,
"arrowParens": "avoid"
}
+49
View File
@@ -0,0 +1,49 @@
language: objective-c
osx_image: xcode8.3
install:
- nvm install 7
- rm -Rf "${TMPDIR}/jest_preprocess_cache"
- brew install yarn --ignore-dependencies
- brew install watchman
- yarn install
script:
- if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait 30 ./scripts/objc-test-ios.sh test; fi
- if [[ "$TEST_TYPE" = objc-tvos ]]; then travis_retry travis_wait 30 ./scripts/objc-test-tvos.sh; fi
- if [[ "$TEST_TYPE" = e2e-objc ]]; then node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3; fi
- if [[ ( "$TEST_TYPE" = podspecs ) && ( "$TRAVIS_PULL_REQUEST" = "false" ) ]]; then gem install cocoapods && travis_wait 30 ./scripts/process-podspecs.sh; fi
cache:
- cocoapods
- yarn
matrix:
- fast_finish: true # Fail the whole build as soon as one test type fails. Should help with Travis capacity issues (very long queues).
# The order of these tests says which are more likely to run first and fail the whole build fast.
env:
- TEST_TYPE=objc-ios
- TEST_TYPE=podspecs
- TEST_TYPE=e2e-objc
- TEST_TYPE=objc-tvos
branches:
only:
- master
- /^.*-stable$/
notifications:
email:
recipients:
- douglowder@mac.com # Doug Lowder built and maintains Apple TV specific code and wants to be notified about tvOS failures.
- eloy@artsy.net # Eloy Durán maintains the podspecs test and wants to be notified about failures.
on_failure: change
on_success: change
slack:
secure: oQL2C966v7/DtxNqfM7WowjY0R5mgLHR2qHkoucwK5iVrmaptnHr8fq01xlj7VT0kDwNLqT3n4+gtCviGw89lq71m3W76c8Pms/10jpjw+LwAfQPVizNw/Bx8MFNNmjDauK/auFxaybiLZupi7zd4xFGOZvScmFdfD4CAAp2OOA=
on_pull_requests: false
on_failure: change
on_success: change
webhooks: https://code.facebook.com/travis/webhook/
-77
View File
@@ -1,77 +0,0 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource-conduct@fb.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
+170 -64
View File
@@ -1,111 +1,217 @@
# Contributing to React Native
Thank you for your interest in contributing to React Native! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome. We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](ECOSYSTEM.md) that goes beyond the main React Native GitHub repository.
<!-- generated_contributing_start -->
React Native is one of Facebook's first open source projects that is both under very active development and is also being used to ship code to everybody using Facebook's mobile apps. If you're interested in contributing to React Native, hopefully this document makes the process for contributing clear.
The [Open Source Guides](https://opensource.guide/) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open source project. Contributors and people new to open source alike will find the following guides especially useful:
## [Code of Conduct](https://code.facebook.com/codeofconduct)
* [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
* [Building Welcoming Communities](https://opensource.guide/building-community/)
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
## Get involved
### [Code of Conduct](https://github.com/facebook/react/blob/master/CODE_OF_CONDUCT.md)
There are many ways to contribute to React Native, and many of them do not involve writing any code. Here's a few ideas to get started:
As a reminder, all contributors are expected to adhere to the [Code of Conduct](https://github.com/facebook/react/blob/master/CODE_OF_CONDUCT.md).
* Simply start using React Native. Go through the [Getting Started](http://facebook.github.io/react-native/docs/getting-started.html) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](http://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues).
* Look through the [open issues](https://github.com/facebook/react-native/issues). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](http://facebook.github.io/react-native/docs/contributing.html#triaging-issues-and-pull-requests).
* If you find an issue you would like to fix, [open a pull request](http://facebook.github.io/react-native/docs/contributing.html#your-first-pull-request). Issues tagged as [_Good First Task_](https://github.com/facebook/react-native/labels/Good%20First%20Task) are a good place to get started.
* Read through the [React Native docs](http://facebook.github.io/react-native/docs). If you find anything that is confusing or can be improved, you can make edits by clicking "Improve this page" at the bottom of most docs.
* Browse [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native) and answer questions. This will help you get familiarized with common pitfalls or misunderstandings, which can be useful when contributing updates to the documentation.
* Take a look at the [features requested](https://react-native.canny.io/feature-requests) by others in the community and consider opening a pull request if you see something you want to work on.
## Ways to Contribute
Contributions are very welcome. If you think you need help planning your contribution, please hop into [#react-native](https://discord.gg/0ZcbPKXt5bZjGY5n) and let people know you're looking for a mentor.
If you are eager to start contributing code right away, we have a list of [good first issues](https://github.com/facebook/react-native/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. As you gain more experience and demonstrate a commitment to evolving React Native, you may be granted issue management permissions in the repository.
Core contributors to React Native meet monthly and post their meeting notes on the [React Native blog](https://facebook.github.io/react-native/blog). You can also find ad hoc discussions in the [React Native Core Contributors](https://www.facebook.com/groups/reactnativeoss/) Facebook group.
There are other ways you can contribute without writing a single line of code. Here are a few things you can do to help out:
### Triaging issues and pull requests
1. **Replying and handling open issues.** We get a lot of issues every day, and some of them may lack necessary information. You can help out by guiding people through the process of filling out the issue template, asking for clarifying information, or pointing them to existing issues that match their description of the problem. We cover more about this process in the [Issue Triage wiki](https://github.com/facebook/react-native/wiki/Issues#triage).
2. **Reviewing pull requests for the docs.** Reviewing [documentation updates](https://github.com/facebook/react-native-website/pulls) can be as simple as checking for spelling and grammar. If you encounter situations that can be explained better in the docs, click **Edit** at the top of most docs pages to get started with your own contribution.
3. **Help people write test plans.** Some pull requests sent to the main repository may lack a proper test plan. These help reviewers understand how the change was tested, and can speed up the time it takes for a contribution to be accepted.
One great way you can contribute to the project without writing any code is to help triage issues and pull requests as they come in.
Each of these tasks is highly impactful, and maintainers will greatly appreciate your help.
* Ask for more information if the issue does not provide all the details required by the template.
* Suggest [labels](https://github.com/facebook/react-native/labels) that can help categorize issues.
* Flag issues that are stale or that should be closed.
* Ask for test plans and review code.
### Our Development Process
You can learn more about handling issues in the [maintainer's guide](docs/maintainers.html#handling-issues).
We use GitHub issues and pull requests to keep track of bug reports and contributions from the community. All changes from engineers at Facebook will sync to [GitHub](https://github.com/facebook/react-native) through a bridge with Facebook's internal source control. Changes from the community are handled through GitHub pull requests. Once a change made on GitHub is approved, it will first be imported into Facebook's internal source control and tested against Facebook's codebase. Once merged at Facebook, the change will eventually sync back to GitHub as a single commit once it has passed Facebook's internal tests.
## Our development process
You can learn more about the contribution process in the following documents:
Some of the core team will be working directly on [GitHub](https://github.com/facebook/react-native). These changes will be public from the beginning. Other changesets will come via a bridge with Facebook's internal source control. This is a necessity as it allows engineers at Facebook outside of the core team to move fast and contribute from an environment they are comfortable in.
* [Issues](https://github.com/facebook/react-native/wiki/Issues)
* [Pull Requests](https://github.com/facebook/react-native/wiki/Pull-Requests)
When a change made on GitHub is approved, it will first be imported into Facebook's internal source control. The change will eventually sync back to GitHub as a single commit once it has passed all internal tests.
We also have a thriving community of contributors who would be happy to help you get set up. You can reach out to us through [@ReactNative](http://twitter.com/reactnative) (the React Native team) and [@ReactNativeComm](http://twitter.com/reactnativecomm) (the React Native Community organization).
### Branch organization
### Repositories
We will do our best to keep `master` in good shape, with tests passing at all times. But in order to move fast, we will make API changes that your application might not be compatible with. We will do our best to [communicate these changes](https://github.com/facebook/react-native/releases) and version appropriately so you can lock into a specific version if need be.
The main repository, <https://github.com/facebook/react-native>, contains the React Native framework itself and it is here where we keep track of bug reports and manage pull requests.
To see what changes are coming and provide better feedback to React Native contributors, use the [latest release candidate](http://facebook.github.io/react-native/versions.html) when possible. By the time a release candidate is released, the changes it contains will have been shipped in production Facebook apps for over two weeks.
There are a few other repositories you might want to familiarize yourself with:
## Bugs
* **React Native website** which contains the source code for the website, including the documentation, located at <https://github.com/facebook/react-native-website>
* **Releases** are coordinated through the <https://github.com/react-native-community/react-native-releases> repository. This includes important documents such as the Changelog.
* **Discussions** about the future of React Native take place in the <https://github.com/react-native-community/discussions-and-proposals> repository.
* **High-quality plugins** for React Native can be found throughout the [React Native Community GitHub Organization](http://github.com/react-native-community/).
We use [GitHub Issues](https://github.com/facebook/react-native/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](http://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues).
Browsing through these repositories should provide some insight into how the React Native open source project is managed.
If you have questions about using React Native, the [Community page](http://facebook.github.io/react-native/support.html) list various resources that should help you get started.
## GitHub Issues
We also have a [place where you can request features or enhancements](https://react-native.canny.io/feature-requests). If you see anything you'd like to be implemented, vote it up and explain your use case.
We use GitHub issues to track bugs exclusively. We have documented our issue handling processes in the [Issues wiki](https://github.com/facebook/react-native/wiki/Issues).
## Reporting new issues
### Security Bugs
When [opening a new issue](https://github.com/facebook/react-native/issues/new), always make sure to fill out the [issue template](https://raw.githubusercontent.com/facebook/react-native/master/.github/ISSUE_TEMPLATE.md). **This step is very important!** Not doing so may result in your issue getting closed. Don't take this personally if this happens, and feel free to open a new issue once you've gathered all the information required by the template.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue.
* **One issue, one bug:** Please report a single bug per issue.
* **Provide a Snack:** The best way to get attention on your issue is to provide a reduced test case. You can use [Snack](https://snack.expo.io/) to demonstrate the issue.
* **Provide reproduction steps:** List all the steps necessary to reproduce the issue. Provide a Snack or upload a sample project to GitHub. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort.
* **Try out the latest version:** Verify that the issue can be reproduced locally by updating your project to use [React Native from `master`](http://facebook.github.io/react-native/versions.html). The bug may have already been fixed!
## Helping with Documentation
We're not able to provide support through GitHub Issues. If you're looking for help with your code, consider asking on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) or reaching out to the community through [other channels](https://facebook.github.io/react-native/support.html).
The React Native documentation is hosted as part of the React Native website repository at https://github.com/facebook/react-native-website. The website itself is located at <https://reactnative.dev/> and it is built using [Docusaurus](https://docusaurus.io/). If there's anything you'd like to change in the docs, you can get started by clicking on the "Edit" button located on the upper right of most pages in the website.
### Security bugs
If you are adding new functionality or introducing a change in behavior, we will ask you to update the documentation to reflect your changes.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. With that in mind, please do not file public issues; go through the process outlined on that page.
### Contributing to the Blog
## Pull requests
The React Native blog is generated [from the Markdown sources for the blog](https://github.com/facebook/react-native-website/tree/master/website/blog).
### Your first pull request
Please open an issue in the https://github.com/facebook/react-native-website repository or tag us on [@ReactNative on Twitter](http://twitter.com/reactnative) and get the go-ahead from a maintainer before writing an article intended for the React Native blog. In most cases, you might want to share your article on your own blog or writing medium instead. It's worth asking, though, in case we find your article is a good fit for the blog.
So you have decided to contribute code back to upstream by opening a pull request. You've invested a good chunk of time, and we appreciate it. We will do our best to work with you and get the PR looked at.
We recommend referring to the [CONTRIBUTING](https://github.com/facebook/react-native-website/blob/master/CONTRIBUTING.md) document for the `react-native-website` repository to learn more about contributing to the website in general.
Working on your first Pull Request? You can learn how from this free video series:
## Contributing Code
[**How to Contribute to an Open Source Project on GitHub**](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
Code-level contributions to React Native generally come in the form of [pull requests](https://help.github.com/en/articles/about-pull-requests). These are done by forking the repo and making changes locally. Directly in the repo, there is the [`rn-tester` app](/packages/rn-tester) that you can install on your device (or simulators) and use to test the changes you're making to React Native sources.
We have a list of [beginner friendly issues](https://github.com/facebook/react-native/labels/Good%20First%20Task) to help you get your feet wet in the React Native codebase and familiar with our contribution process. This is a great place to get started.
The process of proposing a change to React Native can be summarized as follows:
### Proposing a change
1. Fork the React Native repository and create your branch from `master`.
2. Make the desired changes to React Native sources. Use the `packages/rn-tester` app to test them out.
3. If you've added code that should be tested, add tests.
4. If you've changed APIs, update the documentation, which lives in [another repo](https://github.com/facebook/react-native-website/).
5. Ensure the test suite passes, either locally or on CI once you opened a pull request.
6. Make sure your code lints (for example via `yarn lint --fix`).
7. Push the changes to your fork.
8. Create a pull request to the React Native repository.
9. Review and address comments on your pull request.
1. A bot may comment with suggestions. Generally we ask you to resolve these first before a maintainer will review your code.
10. If you haven't already, please complete the [Contributor License Agreement](https://github.com/facebook/react-native/wiki/Contributor-License-Agreement) ("CLA"). **[Complete your CLA here.](https://code.facebook.com/cla)**
If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, we have a [place to track feature requests](https://react-native.canny.io/feature-requests).
If all goes well, your pull request will be merged. If it is not merged, maintainers will do their best to explain the reason why.
If you intend to change the public API, or make any non-trivial changes to the implementation, we recommend [filing an issue](https://github.com/facebook/react-native/issues/new?title=%5BProposal%5D) that includes `[Proposal]` in the title. This lets us reach an agreement on your proposal before you put significant effort into it. These types of issues should be rare. If you have been contributing to the project long enough, you will probably already have access to the [React Native Core Contributors](https://www.facebook.com/groups/reactnativeoss/) Facebook Group, where this sort of discussion is usually held.
### Step-by-step Guide
If you're only fixing a bug, it's fine to submit a pull request right away but we still recommend to file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue.
Whenever you are ready to contribute code, check out our [step-by-step guide to sending your first pull request](https://github.com/facebook/react-native/wiki/Pull-Requests#getting-ready-to-submit-your-first-pull-request), or read the [How to Contribute Code](https://github.com/facebook/react-native/wiki/How-to-Contribute) wiki for more details.
### Sending a pull request
### Tests
Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.
Tests help us prevent regressions from being introduced to the codebase. The GitHub repository is continuously tested using Circle and Appveyor, the results of which are available through the Checks functionality on [commits](https://github.com/facebook/react-native/commits/master) and pull requests. You can learn more about running and writing tests in the [Tests wiki](http://github.com/facebook/react-native/wiki/Tests).
**Before submitting a pull request**, please make sure the following is done:
## Community Contributions
1. Fork [the repository](https://github.com/facebook/react-native) and create your branch from `master`.
2. Add the copyright notice to the top of any new files you've added.
3. Describe your [**test plan**](https://facebook.github.io/react-native/docs/contributing.html#test-plan) in your commit.
4. Ensure [**tests pass**](https://facebook.github.io/react-native/docs/contributing.html#contrinuous-integration-tests) on both Travis and Circle CI.
5. Make sure your code lints (`npm run lint`).
6. If you haven't already, [sign the CLA](https://code.facebook.com/cla).
Contributions to React Native are not limited to GitHub. You can help others by sharing your experience using React Native, whether that is through blog posts, presenting talks at conferences, or simply sharing your thoughts on Twitter and tagging @ReactNative.
All pull requests should be opened against the `master` branch.
## Where to Get Help
> **Note:** It is not necessary to keep clicking `Merge master to your branch` on the PR page. You would want to merge master if there are conflicts or tests are failing. The Facebook-GitHub-Bot ultimately squashes all commits to a single one before merging your PR.
As you work on React Native, it is natural that sooner or later you may require help. In addition to the resources listed in [SUPPORT](.github/SUPPORT.md), people interested in contributing may take advantage of the following:
#### Test plan
* **Twitter**. The React Native team at Facebook has its own account at [@reactnative](https://twitter.com/reactnative), and the React Native Community uses [@reactnativecomm](https://twitter.com/reactnativecomm). If you feel stuck, or need help contributing, please do not hesitate to reach out.
* **Proposals Repository**. If you are considering working on a feature large in scope, consider [creating a proposal first](https://github.com/react-native-community/discussions-and-proposals). The community can help you figure out the right approach, and we'd be happy to help.
* **React Native Community Discord**. While we try to hold most discussions in public, sometimes it can be beneficial to have conversations in real time with other contributors. People who have demonstrated a commitment to moving React Native forward through sustained contributions to the project may eventually be invited to join the React Native Community Discord.
A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI or updates the website.
* If you've added code that should be tested, add tests!
* If you've changed APIs, update the documentation.
* If you've updated the docs, verify the website locally and submit screenshots if applicable (see [website/README.md](https://github.com/facebook/react-native/blob/master/website/README.md))
See [What is a Test Plan?](https://medium.com/@martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9) to learn more.
#### Continuous integration tests
Make sure all **tests pass** on both [Travis][travis] and [Circle CI][circle]. PRs that break tests are unlikely to be merged. Learn more about [testing your changes here](https://facebook.github.io/react-native/docs/testing.html).
[travis]: https://travis-ci.org/facebook/react-native
[circle]: http://circleci.com/gh/facebook/react-native
#### Breaking changes
When adding a new breaking change, follow this template in your pull request:
```
### New breaking change here
* **Who does this affect**:
* **How to migrate**:
* **Why make this breaking change**:
* **Severity (number of people affected x effort)**:
```
If your pull request is merged, a core contributor will update the [list of breaking changes](https://github.com/facebook/react-native/wiki/Breaking-Changes) which is then used to populate the release notes.
#### Copyright Notice for files
Copy and paste this to the top of your new file(s):
```JS
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
```
If you've added a new module, add a `@providesModule <moduleName>` at the end of the comment. This will allow the haste package manager to find it.
#### Contributor License Agreement (CLA)
In order to accept your pull request, we need you to submit a CLA. You only need to do this once, so if you've done this for another Facebook open source project, you're good to go. If you are submitting a pull request for the first time, the Facebook GitHub Bot will reply with a link to the CLA form. You may also [complete your CLA here](https://code.facebook.com/cla).
### What happens next?
The core team will be monitoring for pull requests. Read [what to expect from maintainers](https://facebook.github.io/react-native/docs/maintainers.html#handling-pull-requests) to understand what may happen after you open a pull request.
## Style Guide
Our linter will catch most styling issues that may exist in your code. You can check the status of your code styling by simply running `npm run lint`.
However, there are still some styles that the linter cannot pick up.
### Code Conventions
#### General
* **Most important: Look around.** Match the style you see used in the rest of the project. This includes formatting, naming things in code, naming things in documentation.
* Add trailing commas,
* 2 spaces for indentation (no tabs)
* "Attractive"
#### JavaScript
* Use semicolons;
* `'use strict';`
* Prefer `'` over `"`
* Do not use the optional parameters of `setTimeout` and `setInterval`
* 80 character line length
#### JSX
* Prefer `"` over `'` for string literal props
* When wrapping opening tags over multiple lines, place one prop per line
* `{}` of props should hug their values (no spaces)
* Place the closing `>` of opening tags on the same line as the last prop
* Place the closing `/>` of self-closing tags on their own line and left-align them with the opening `<`
#### Objective-C
* Space after `@property` declarations
* Brackets on *every* `if`, on the *same* line
* `- method`, `@interface`, and `@implementation` brackets on the following line
* *Try* to keep it around 80 characters line length (sometimes it's just not possible...)
* `*` operator goes with the variable name (e.g. `NSObject *variableName;`)
#### Java
* If a method call spans multiple lines closing bracket is on the same line as the last argument.
* If a method header doesn't fit on one line each argument goes on a separate line.
* 100 character line length
### Documentation
* Do not wrap lines at 80 characters - configure your editor to soft-wrap when editing documentation.
## License
By contributing to React Native, you agree that your contributions will be licensed under its BSD license.
<!-- generated_contributing_end -->
+54
View File
@@ -0,0 +1,54 @@
FROM containership/android-base:latest
# set default environment variables
ENV GRADLE_OPTS="-Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
# add ReactAndroid directory
ADD .buckconfig /app/.buckconfig
ADD .buckjavaargs /app/.buckjavaargs
ADD ReactAndroid /app/ReactAndroid
ADD ReactCommon /app/ReactCommon
ADD keystores /app/keystores
# set workdir
WORKDIR /app
# run buck fetches
RUN buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
RUN buck fetch ReactAndroid/src/test/...
RUN buck fetch ReactAndroid/src/androidTest/...
# build app
RUN buck build ReactAndroid/src/main/java/com/facebook/react
RUN buck build ReactAndroid/src/main/java/com/facebook/react/shell
ADD gradle /app/gradle
ADD gradlew /app/gradlew
ADD settings.gradle /app/settings.gradle
ADD build.gradle /app/build.gradle
ADD react.gradle /app/react.gradle
# run gradle downloads
RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
# compile native libs with Gradle script, we need bridge for unit and integration tests
RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1 -Pcom.android.build.threadPoolSize=1
# add all react-native code
ADD . /app
WORKDIR /app
# https://github.com/npm/npm/issues/13306
RUN cd $(npm root -g)/npm && npm install fs-extra && sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js
# build node dependencies
RUN npm install
RUN npm install github@0.2.4
WORKDIR /app/website
RUN npm install
WORKDIR /app
+101
View File
@@ -0,0 +1,101 @@
FROM library/ubuntu:16.04
# set default build arguments
ARG ANDROID_VERSION=25.2.3
ARG BUCK_VERSION=f3452a6a7ab15a60e94c962e686293acbe677473
ARG NDK_VERSION=10e
ARG NODE_VERSION=6.2.0
ARG WATCHMAN_VERSION=4.7.0
# set default environment variables
ENV ADB_INSTALL_TIMEOUT=10
ENV PATH=${PATH}:/opt/buck/bin/
ENV ANDROID_HOME=/opt/android
ENV ANDROID_SDK_HOME=${ANDROID_HOME}
ENV PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
ENV ANDROID_NDK=/opt/ndk/android-ndk-r$NDK_VERSION
ENV PATH=${PATH}:${ANDROID_NDK}
# install system dependencies
RUN apt-get update && apt-get install ant autoconf automake curl g++ gcc git libqt5widgets5 lib32z1 lib32stdc++6 make maven npm openjdk-8* python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev unzip -y
# configure npm
RUN npm config set spin=false
RUN npm config set progress=false
# install node
RUN npm install n -g
RUN n $NODE_VERSION
# download buck
RUN git clone https://github.com/facebook/buck.git /opt/buck
WORKDIR /opt/buck
RUN git checkout $BUCK_VERSION
# build buck
RUN ant
# download watchman
RUN git clone https://github.com/facebook/watchman.git /opt/watchman
WORKDIR /opt/watchman
RUN git checkout v$WATCHMAN_VERSION
# build watchman
RUN ./autogen.sh
RUN ./configure
RUN make
RUN make install
# download and unpack android
RUN mkdir /opt/android
WORKDIR /opt/android
RUN curl --silent https://dl.google.com/android/repository/tools_r$ANDROID_VERSION-linux.zip > android.zip
RUN unzip android.zip
RUN rm android.zip
# download and unpack NDK
RUN mkdir /opt/ndk
WORKDIR /opt/ndk
RUN curl --silent https://dl.google.com/android/repository/android-ndk-r$NDK_VERSION-linux-x86_64.zip > ndk.zip
RUN unzip ndk.zip
# cleanup NDK
RUN rm ndk.zip
# Add android SDK tools
# Android SDK Platform-tools, revision 25.0.4
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android SDK Platform-tools, revision 25.0.4" | awk '{ print $1 }' | sed 's/.$//')
# Android SDK Build-tools, revision 23.0.1
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android SDK Build-tools, revision 23.0.1" | awk '{ print $1 }' | sed 's/.$//')
# SDK Platform Android 6.0, API 23, revision 3
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "SDK Platform Android 6.0, API 23" | awk '{ print $1 }' | sed 's/.$//')
# SDK Platform Android 4.4.2, API 19, revision 4
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "SDK Platform Android 4.4.2, API 19, revision 4" | awk '{ print $1 }' | sed 's/.$//')
# ARM EABI v7a System Image, Android API 19, revision 5
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "ARM EABI v7a System Image, Android API 19, revision 5" | awk '{ print $1 }' | sed 's/.$//')
# Intel x86 Atom System Image, Android API 19, revision 5
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Intel x86 Atom System Image, Android API 19, revision 5" | awk '{ print $1 }' | sed 's/.$//')
# Google APIs, Android API 23, revision 1
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Google APIs, Android API 23, revision 1" | awk '{ print $1 }' | sed 's/.$//')
# Android Support Repository, revision 45
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android Support Repository" | awk '{ print $1 }' | sed 's/.$//')
# Link adb executable
RUN ln -s /opt/android/platform-tools/adb /usr/bin/adb
# Install google-chrome
RUN curl -fsSL https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update \
&& apt-get install -y google-chrome-stable
# clean up unnecessary directories
RUN rm -rf /opt/android/system-images/android-19/default/x86
+19
View File
@@ -0,0 +1,19 @@
FROM library/node:6.9.2
ENV YARN_VERSION=0.27.5
# install dependencies
RUN apt-get update && apt-get install ocaml libelf-dev -y
RUN npm install yarn@$YARN_VERSION -g
# add code
RUN mkdir /app
ADD . /app
WORKDIR /app
RUN yarn install --ignore-engines
WORKDIR website
RUN yarn install --ignore-engines --ignore-platform
WORKDIR /app
@@ -1,8 +1,10 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
@@ -18,6 +20,7 @@
* --package - com.facebook.react.tests
* --retries [num] - how many times to retry possible flaky commands: npm install and running tests, default 1
*/
/*eslint-disable no-undef */
const argv = require('yargs').argv;
const async = require('async');
@@ -28,7 +31,7 @@ const path = require('path');
const colors = {
GREEN: '\x1b[32m',
RED: '\x1b[31m',
RESET: '\x1b[0m',
RESET: '\x1b[0m'
};
const test_opts = {
@@ -38,11 +41,11 @@ const test_opts = {
PATH: argv.path || './ReactAndroid/src/androidTest/java/com/facebook/react/tests',
RETRIES: parseInt(argv.retries || 2, 10),
TEST_TIMEOUT: parseInt(argv['test-timeout'] || 1000 * 60 * 10, 10),
TEST_TIMEOUT: parseInt(argv['test-timeout'] || 1000 * 60 * 10),
OFFSET: argv.offset,
COUNT: argv.count,
};
COUNT: argv.count
}
let max_test_class_length = Number.NEGATIVE_INFINITY;
@@ -68,6 +71,7 @@ testClasses = testClasses.map((clazz) => {
// only process subset of the tests at corresponding offset and count if args provided
if (test_opts.COUNT != null && test_opts.OFFSET != null) {
const testCount = testClasses.length;
const start = test_opts.COUNT * test_opts.OFFSET;
const end = start + test_opts.COUNT;
@@ -80,15 +84,15 @@ if (test_opts.COUNT != null && test_opts.OFFSET != null) {
}
}
async.mapSeries(testClasses, (clazz, callback) => {
if (clazz.length > max_test_class_length) {
return async.mapSeries(testClasses, (clazz, callback) => {
if(clazz.length > max_test_class_length) {
max_test_class_length = clazz.length;
}
return async.retry(test_opts.RETRIES, (retryCb) => {
const test_process = child_process.spawn('./.circleci/Dockerfiles/scripts/run-instrumentation-tests-via-adb-shell.sh', [test_opts.PACKAGE, clazz], {
stdio: 'inherit',
});
const test_process = child_process.spawn('./ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh', [test_opts.PACKAGE, clazz], {
stdio: 'inherit'
})
const timeout = setTimeout(() => {
test_process.kill();
@@ -102,7 +106,7 @@ async.mapSeries(testClasses, (clazz, callback) => {
test_process.on('exit', (code) => {
clearTimeout(timeout);
if (code !== 0) {
if(code !== 0) {
return retryCb(new Error(`Process exited with code: ${code}`));
}
@@ -111,7 +115,7 @@ async.mapSeries(testClasses, (clazz, callback) => {
}, (err) => {
return callback(null, {
name: clazz,
status: err ? 'failure' : 'success',
status: err ? 'failure' : 'success'
});
});
}, (err, results) => {
@@ -134,16 +138,16 @@ function print_test_suite_results(results) {
function pad_output(num_chars) {
let i = 0;
while (i < num_chars) {
while(i < num_chars) {
process.stdout.write(' ');
i++;
}
}
results.forEach((test) => {
if (test.status === 'success') {
if(test.status === 'success') {
color = colors.GREEN;
passing_suites++;
} else if (test.status === 'failure') {
} else if(test.status === 'failure') {
color = colors.RED;
failing_suites++;
}
@@ -0,0 +1,35 @@
#!/bin/bash
# for buck gen
mount -o remount,exec /dev/shm
AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
# create virtual device
echo no | android create avd -n $AVD_UUID -f -t android-19 --abi default/armeabi-v7a
# emulator setup
emulator64-arm -avd $AVD_UUID -no-skin -no-audio -no-window -no-boot-anim &
bootanim=""
until [[ "$bootanim" =~ "stopped" ]]; do
sleep 5
bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1)
echo "boot animation status=$bootanim"
done
set -x
# solve issue with max user watches limit
echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches
watchman shutdown-server
# integration tests
# build JS bundle for instrumentation tests
node local-cli/cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
# build test APK
source ./scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
# run installed apk with tests
node ./ContainerShip/scripts/run-android-ci-instrumentation-tests.js $*
exit $?
@@ -0,0 +1,12 @@
#!/bin/bash
# set default environment variables
UNIT_TESTS_BUILD_THREADS="${UNIT_TESTS_BUILD_THREADS:-1}"
# for buck gen
mount -o remount,exec /dev/shm
set -x
# run unit tests
buck test ReactAndroid/src/test/... --config build.threads=$UNIT_TESTS_BUILD_THREADS
+253
View File
@@ -0,0 +1,253 @@
#!/bin/bash
set -ex
# set default environment variables
ROOT=$(pwd)
SCRIPTS=$(pwd)/scripts
RUN_ANDROID=0
RUN_CLI_INSTALL=1
RUN_IOS=0
RUN_JS=0
RETRY_COUNT=${RETRY_COUNT:-2}
AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
ANDROID_NPM_DEPS="appium@1.5.1 mocha@2.4.5 wd@0.3.11 colors@1.0.3 pretty-data2@0.40.1"
CLI_PACKAGE=$ROOT/react-native-cli/react-native-cli-*.tgz
PACKAGE=$ROOT/react-native-*.tgz
# solve issue with max user watches limit
echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches
watchman shutdown-server
# retries command on failure
# $1 -- max attempts
# $2 -- command to run
function retry() {
local -r -i max_attempts="$1"; shift
local -r cmd="$@"
local -i attempt_num=1
until $cmd; do
if (( attempt_num == max_attempts )); then
echo "Execution of '$cmd' failed; no more attempts left"
return 1
else
(( attempt_num++ ))
echo "Execution of '$cmd' failed; retrying for attempt number $attempt_num..."
fi
done
}
# parse command line args & flags
while :; do
case "$1" in
--android)
RUN_ANDROID=1
shift
;;
--ios)
RUN_IOS=1
shift
;;
--js)
RUN_JS=1
shift
;;
--skip-cli-install)
RUN_CLI_INSTALL=0
shift
;;
--tvos)
RUN_IOS=1
shift
;;
*)
break
esac
done
function e2e_suite() {
cd $ROOT
if [ $RUN_ANDROID -eq 0 ] && [ $RUN_IOS -eq 0 ] && [ $RUN_JS -eq 0 ]; then
echo "No e2e tests specified!"
return 0
fi
# create temp dir
TEMP_DIR=$(mktemp -d /tmp/react-native-XXXXXXXX)
# To make sure we actually installed the local version
# of react-native, we will create a temp file inside the template
# and check that it exists after `react-native init
IOS_MARKER=$(mktemp $ROOT/local-cli/templates/HelloWorld/ios/HelloWorld/XXXXXXXX)
ANDROID_MARKER=$(mktemp ${ROOT}/local-cli/templates/HelloWorld/android/XXXXXXXX)
# install CLI
cd react-native-cli
npm pack
cd ..
# can skip cli install for non sudo mode
if [ $RUN_CLI_INSTALL -ne 0 ]; then
npm install -g $CLI_PACKAGE
if [ $? -ne 0 ]; then
echo "Could not install react-native-cli globally, please run in su mode"
echo "Or with --skip-cli-install to skip this step"
return 1
fi
fi
if [ $RUN_ANDROID -ne 0 ]; then
set +ex
# create virtual device
if ! android list avd | grep "$AVD_UUID" > /dev/null; then
echo no | android create avd -n $AVD_UUID -f -t android-19 --abi default/armeabi-v7a
fi
# newline at end of adb devices call and first line is headers
DEVICE_COUNT=$(adb devices | wc -l)
((DEVICE_COUNT -= 2))
# will always kill an existing emulator if one exists for fresh setup
if [[ $DEVICE_COUNT -ge 1 ]]; then
adb emu kill
fi
# emulator setup
emulator64-arm -avd $AVD_UUID -no-skin -no-audio -no-window -no-boot-anim &
bootanim=""
until [[ "$bootanim" =~ "stopped" ]]; do
sleep 5
bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1)
echo "boot animation status=$bootanim"
done
set -ex
./gradlew :ReactAndroid:installArchives -Pjobs=1 -Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"
if [ $? -ne 0 ]; then
echo "Failed to compile Android binaries"
return 1
fi
fi
npm pack
if [ $? -ne 0 ]; then
echo "Failed to pack react-native"
return 1
fi
cd $TEMP_DIR
retry $RETRY_COUNT react-native init EndToEndTest --version $PACKAGE --npm
if [ $? -ne 0 ]; then
echo "Failed to execute react-native init"
echo "Most common reason is npm registry connectivity, try again"
return 1
fi
cd EndToEndTest
# android tests
if [ $RUN_ANDROID -ne 0 ]; then
echo "Running an Android e2e test"
echo "Installing e2e framework"
retry $RETRY_COUNT npm install --save-dev $ANDROID_NPM_DEPS --silent >> /dev/null
if [ $? -ne 0 ]; then
echo "Failed to install appium"
echo "Most common reason is npm registry connectivity, try again"
return 1
fi
cp $SCRIPTS/android-e2e-test.js android-e2e-test.js
cd android
echo "Downloading Maven deps"
./gradlew :app:copyDownloadableDepsToLibs
cd ..
keytool -genkey -v -keystore android/keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"
node ./node_modules/.bin/appium >> /dev/null &
APPIUM_PID=$!
echo "Starting appium server $APPIUM_PID"
echo "Building app"
buck build android/app
# hack to get node unhung (kill buckd)
kill -9 $(pgrep java)
if [ $? -ne 0 ]; then
echo "could not execute Buck build, is it installed and in PATH?"
return 1
fi
echo "Starting packager server"
npm start >> /dev/null &
SERVER_PID=$!
sleep 15
echo "Executing android e2e test"
retry $RETRY_COUNT node node_modules/.bin/_mocha android-e2e-test.js
if [ $? -ne 0 ]; then
echo "Failed to run Android e2e tests"
echo "Most likely the code is broken"
return 1
fi
# kill packager process
if kill -0 $SERVER_PID; then
echo "Killing packager $SERVER_PID"
kill -9 $SERVER_PID
fi
# kill appium process
if kill -0 $APPIUM_PID; then
echo "Killing appium $APPIUM_PID"
kill -9 $APPIUM_PID
fi
fi
# ios tests
if [ $RUN_IOS -ne 0 ]; then
echo "Running ios e2e tests not yet implemented for docker!"
fi
# js tests
if [ $RUN_JS -ne 0 ]; then
# Check the packager produces a bundle (doesn't throw an error)
react-native bundle --max-workers 1 --platform android --dev true --entry-file index.js --bundle-output android-bundle.js
if [ $? -ne 0 ]; then
echo "Could not build android bundle"
return 1
fi
react-native bundle --max-workers 1 --platform ios --dev true --entry-file index.js --bundle-output ios-bundle.js
if [ $? -ne 0 ]; then
echo "Could not build iOS bundle"
return 1
fi
fi
# directory cleanup
rm $IOS_MARKER
rm $ANDROID_MARKER
return 0
}
retry $RETRY_COUNT e2e_suite
@@ -1,10 +1,5 @@
#!/bin/bash
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# shellcheck disable=SC1117
# Python script to run instrumentation tests, copied from https://github.com/circleci/circle-dummy-android
# Example: ./scripts/run-android-instrumentation-tests.sh com.facebook.react.tests com.facebook.react.tests.ReactPickerTestCase
#
@@ -14,7 +9,7 @@ export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH"
adb logcat -c
# run tests and check output
python - "$1" "$2" << END
python - $1 $2 << END
import re
import subprocess as sp
@@ -29,12 +24,12 @@ test_class = None
if len(sys.argv) > 2:
test_class = sys.argv[2]
def update():
# prevent CircleCI from killing the process for inactivity
while not done:
time.sleep(5)
print "Running in background. Waiting for 'adb' command response..."
print "Running in background. Waiting for 'adb' command reponse..."
t = threading.Thread(target=update)
t.dameon = True
@@ -43,10 +38,10 @@ t.start()
def run():
sp.Popen(['adb', 'wait-for-device']).communicate()
if (test_class != None):
p = sp.Popen('adb shell am instrument -w -e class %s %s/android.support.test.runner.AndroidJUnitRunner'
p = sp.Popen('adb shell am instrument -w -e class %s %s/android.support.test.runner.AndroidJUnitRunner'
% (test_class, test_app), shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
else :
p = sp.Popen('adb shell am instrument -w %s/android.support.test.runner.AndroidJUnitRunner'
p = sp.Popen('adb shell am instrument -w %s/android.support.test.runner.AndroidJUnitRunner'
% (test_app), shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
return p.communicate()
+96
View File
@@ -0,0 +1,96 @@
# Dockerfile Tests
This is a high level overview of the test configuration using docker. It explains how to run the tests locally
and how they integrate with the Jenkins Pipeline script to run the automated tests on ContainerShip <https://www.containership.io/>.
## Docker Installation
It is required to have Docker running on your machine in order to build and run the tests in the Dockerfiles.
See <https://docs.docker.com/engine/installation/> for more information on how to install.
## Convenience NPM Run Scripts
We have added a number of default run scripts to the `package.json` file to simplify building and running your tests.
`npm run test-android-setup` - Pulls down the base android docker image used for running the tests
`npm run test-android-build` - Builds the docker image used to run the tests
`npm run test-android-run-unit` - Runs all the unit tests that have been built in the latest react/android docker image (note: you need to run test-android-build before executing this, if the image does not exist it will fail)
`npm run test-android-run-instrumentation` - Runs all the instrumentation tests that have been built in the latest react/android docker image (note: you need to run test-android-build before executing this, if the image does not exist it will fail). You can also pass additional flags to filter which tests instrumentation tests are run. Ex: `npm run test-android-run-instrumentation -- --filter=TestIdTestCase` to only run the TestIdTestCase instrumentation test. See below for more information
on the instrumentation test flags.
`npm run test-android-run-e2e` - Runs all the end to end tests that have been built in the latest react/android docker image (note: you need to run test-android-build before executing this, if the image does not exist it will fail)
`npm run test-android-unit` - Builds and runs the android unit tests.
`npm run test-android-instrumentation` - Builds and runs the android instrumentation tests.
`npm run test-android-e2e` - Builds and runs the android end to end tests.
## Detailed Android Setup
There are two Dockerfiles for use with the Android codebase.
The `Dockerfile.android-base` contains all the necessary prerequisites required to run the React Android tests. It is
separated out into a separate Dockerfile because these are dependencies that rarely change and also because it is quite
a beastly image since it contains all the Android depedencies for running android and the emulators (~9GB).
The good news is you should rarely have to build or pull down the base image! All iterative code updates happen as
part of the `Dockerfile.android` image build.
So step one...
`docker pull containership/android-base:latest`
This will take quite some time depending on your connection and you need to ensure you have ~10GB of free disk space.
Once this is done, you can run tests locally by executing two simple commands:
1. `docker build -t react/android -f ./ContainerShip/Dockerfile.android .`
2. `docker run --cap-add=SYS_ADMIN -it react/android bash ContainerShip/scripts/run-android-docker-unit-tests.sh`
> Note: `--cap-add=SYS_ADMIN` flag is required for the `ContainerShip/scripts/run-android-docker-unit-tests.sh` and
`ContainerShip/scripts/run-android-docker-instrumentation-tests.sh` in order to allow the remounting of `/dev/shm` as writeable
so the `buck` build system may write temporary output to that location
Every time you make any modifications to the codebase, you should re-run the `docker build ...` command in order for your
updates to be included in your local docker image.
The following shell scripts have been provided for android testing:
`ContainerShip/scripts/run-android-docker-unit-tests.sh` - Runs the standard android unit tests
`ContainerShip/scripts/run-android-docker-instrumentation-tests.sh` - Runs the android instrumentation tests on the emulator. *Note* that these
tests take quite some time to run so there are various flags you can pass in order to filter which tests are run (see below)
`ContainerShip/scripts/run-ci-e2e-tests.sh` - Runs the android end to end tests
#### ContainerShip/scripts/run-android-docker-instrumentation-tests.sh
The instrumentation test script accepts the following flags in order to customize the execution of the tests:
`--filter` - A regex that filters which instrumentation tests will be run. (Defaults to .*)
`--package` - Name of the java package containing the instrumentation tests (Defaults to com.facebook.react.tests)
`--path` - Path to the directory containing the instrumentation tests. (Defaults to ./ReactAndroid/src/androidTest/java/com/facebook/react/tests)
`--retries` - Number of times to retry a failed test before declaring a failure (Defaults to 2)
For example, if locally you only wanted to run the InitialPropsTestCase, you could do the following:
`docker run --cap-add=SYS_ADMIN -it react/android bash ContainerShip/scripts/run-android-docker-instrumentation-tests.sh --filter="InitialPropsTestCase"`
# Javascript Setup
There is a single Dockerfile for use with the javascript codebase.
The `Dockerfile.javascript` base requires all the necessary dependencies for running Javascript tests.
Any time you make an update to the codebase, you can build and run the javascript tests with the following three commands:
1. `docker build -t react/js -f ./ContainerShip/Dockerfile.javascript .`
2. `docker run -it react/js yarn test --maxWorkers=4`
3. `docker run -it react/js yarn run flow -- check`
-59
View File
@@ -1,59 +0,0 @@
# The React Native Ecosystem
We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but hadn't been written down before.
There are three types of stakeholders:
* **Partners:** Companies that are significantly invested in React Native and have been for years.
* **Core Contributors:** Individual people who contribute to the React Native project.
* **Community Contributors:** Individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization.
## Partners
Partners are companies that are significantly invested in React Native and have been for years. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Partners think of React Native as a product: they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications.
React Native's current set of partners include Callstack, Expo, Facebook, Infinite Red, Microsoft and Software Mansion. Many engineers from these companies are core contributors, and their partner responsibilities also include:
* **[Callstack](https://callstack.com/):** Manages releases, maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and organizes [React Native EU](https://react-native.eu/)
* **[Expo](https://expo.io/):** Builds [expo](https://github.com/expo/expo) on top of React Native to simplify app development
* **[Facebook](https://opensource.facebook.com):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/)
* **[Microsoft](http://aka.ms/reactnative):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS
* **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins.
In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor:
## Core Contributors
Core contributors are individuals who contribute to the React Native project. A core contributor is somebody who displayed a lasting commitment to the evolution and maintenance of React Native. The work done by core contributors includes responsibilities mentioned in the “Partners” section above, and concretely means that they:
* Consistently contribute high quality changes, fixes and improvements
* Actively review changes and provide quality feedback to contributors
* Manage the release process of React Native by maintaining release branches, communicating changes to users and publishing releases
* Love to help out other users with issues on GitHub
* Mentor and encourage first time contributors
* Identify React Native community members who could be effective core contributors
* Help build an inclusive community with people from all backgrounds
* Are great at communicating with other contributors and the community in general
These are behaviors we have observed in our existing core contributors. They aren't strict rules but rather outline their usual responsibilities. We do not expect every core contributor to do all of the above things all the time. Most importantly, we want to create a supportive and friendly environment that fosters collaboration. Above all else, **we are always polite and friendly.**
Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity, a core contributor may be removed.
We aim to make contributing to React Native as easy and transparent as possible. All important topics are handled through a [discussion or RFC process on GitHub](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors.
## Community Contributors
Community contributors are individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization. This organization exists as an incubator for high quality components that extend the capabilities of React Native with functionality that many but not all applications require. Facebook engineers will provide guidance to help build a vibrant community of people and components that make React Native better.
This structure has multiple benefits:
* Keep the core of React Native small, which improves performance and reduces the surface area
* Provide visibility to projects through shared representation, for example on the React Native website or on Twitter
* Ensure a consistent and high standard for code, documentation, user experience, stability and contributions for third-party components
* Upgrade the most important components right away when we make breaking changes and move the ecosystem forward at a fast pace
* Find new maintainers for projects that are important but were abandoned by previous owners
Additionally, some companies may choose to sponsor the development of one or many of the packages that are part of the community organization. They will commit to maintain projects, triage issues, fix bugs and develop features. In turn, they will be able to gain visibility for their work, for example through a mention of active maintainers in the README of individual projects after a consistent period of contributions. Such a mention may be removed if maintainers abandon the project.
If you are working on a popular component and would like to move it to the React Native community, please create an issue on the [discussions-and-proposals repository](https://github.com/react-native-community/discussions-and-proposals).
+30 -31
View File
@@ -1,44 +1,43 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow strict-local
* @flow
* @providesModule AccessibilityManagerTest
*/
'use strict';
import invariant from 'invariant';
import NativeAccessibilityManager from 'react-native/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager';
import {DeviceEventEmitter, NativeModules, View} from 'react-native';
import * as React from 'react';
const React = require('react');
const ReactNative = require('react-native');
const { View } = ReactNative;
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
const {
TestModule,
AccessibilityManager,
} = ReactNative.NativeModules;
const {TestModule} = NativeModules;
class AccessibilityManagerTest extends React.Component<{...}> {
class AccessibilityManagerTest extends React.Component<{}> {
componentDidMount() {
invariant(
NativeAccessibilityManager,
"NativeAccessibilityManager doesn't exist",
);
NativeAccessibilityManager.setAccessibilityContentSizeMultipliers({
extraSmall: 1.0,
small: 2.0,
medium: 3.0,
large: 4.0,
extraLarge: 5.0,
extraExtraLarge: 6.0,
extraExtraExtraLarge: 7.0,
accessibilityMedium: 8.0,
accessibilityLarge: 9.0,
accessibilityExtraLarge: 10.0,
accessibilityExtraExtraLarge: 11.0,
accessibilityExtraExtraExtraLarge: 12.0,
AccessibilityManager.setAccessibilityContentSizeMultipliers({
'extraSmall': 1.0,
'small': 2.0,
'medium': 3.0,
'large': 4.0,
'extraLarge': 5.0,
'extraExtraLarge': 6.0,
'extraExtraExtraLarge': 7.0,
'accessibilityMedium': 8.0,
'accessibilityLarge': 9.0,
'accessibilityExtraLarge': 10.0,
'accessibilityExtraExtraLarge': 11.0,
'accessibilityExtraExtraExtraLarge': 12.0,
});
DeviceEventEmitter.addListener('didUpdateDimensions', update => {
RCTDeviceEventEmitter.addListener('didUpdateDimensions', update => {
TestModule.markTestPassed(update.window.fontScale === 4.0);
});
}
+27 -24
View File
@@ -1,60 +1,63 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @providesModule AppEventsTest
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {NativeAppEventEmitter, StyleSheet, Text, View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var ReactNative = require('react-native');
var {
NativeAppEventEmitter,
StyleSheet,
Text,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
var deepDiffer = require('deepDiffer');
const TEST_PAYLOAD = {foo: 'bar'};
var TEST_PAYLOAD = {foo: 'bar'};
type AppEvent = {
data: Object,
ts: number,
...
};
type AppEvent = { data: Object, ts: number, };
type State = {
sent: 'none' | AppEvent,
received: 'none' | AppEvent,
elapsed?: string,
...
};
class AppEventsTest extends React.Component<{...}, State> {
class AppEventsTest extends React.Component<{}, State> {
state: State = {sent: 'none', received: 'none'};
componentDidMount() {
NativeAppEventEmitter.addListener('testEvent', this.receiveEvent);
const event = {data: TEST_PAYLOAD, ts: Date.now()};
var event = {data: TEST_PAYLOAD, ts: Date.now()};
TestModule.sendAppEvent('testEvent', event);
this.setState({sent: event});
}
receiveEvent: (event: any) => void = (event: any) => {
receiveEvent = (event: any) => {
if (deepDiffer(event.data, TEST_PAYLOAD)) {
throw new Error('Received wrong event: ' + JSON.stringify(event));
}
const elapsed = Date.now() - event.ts + 'ms';
var elapsed = (Date.now() - event.ts) + 'ms';
this.setState({received: event, elapsed}, () => {
TestModule.markTestCompleted();
});
};
render(): React.Node {
render() {
return (
<View style={styles.container}>
<Text>{JSON.stringify(this.state, null, ' ')}</Text>
<Text>
{JSON.stringify(this.state, null, ' ')}
</Text>
</View>
);
}
@@ -62,7 +65,7 @@ class AppEventsTest extends React.Component<{...}, State> {
AppEventsTest.displayName = 'AppEventsTest';
const styles = StyleSheet.create({
var styles = StyleSheet.create({
container: {
margin: 40,
},
+64 -81
View File
@@ -1,57 +1,59 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow
* @providesModule AsyncStorageTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {AsyncStorage, Text, View, StyleSheet} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var ReactNative = require('react-native');
var {
AsyncStorage,
Text,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
var deepDiffer = require('deepDiffer');
const DEBUG = false;
var DEBUG = false;
const KEY_1 = 'key_1';
const VAL_1 = 'val_1';
const KEY_2 = 'key_2';
const VAL_2 = 'val_2';
const KEY_MERGE = 'key_merge';
const VAL_MERGE_1 = {foo: 1, bar: {hoo: 1, boo: 1}, moo: {a: 3}};
const VAL_MERGE_2 = {bar: {hoo: 2}, baz: 2, moo: {a: 3}};
const VAL_MERGE_EXPECT = {foo: 1, bar: {hoo: 2, boo: 1}, baz: 2, moo: {a: 3}};
var KEY_1 = 'key_1';
var VAL_1 = 'val_1';
var KEY_2 = 'key_2';
var VAL_2 = 'val_2';
var KEY_MERGE = 'key_merge';
var VAL_MERGE_1 = {'foo': 1, 'bar': {'hoo': 1, 'boo': 1}, 'moo': {'a': 3}};
var VAL_MERGE_2 = {'bar': {'hoo': 2}, 'baz': 2, 'moo': {'a': 3}};
var VAL_MERGE_EXPECT =
{'foo': 1, 'bar': {'hoo': 2, 'boo': 1}, 'baz': 2, 'moo': {'a': 3}};
// setup in componentDidMount
let done = (result: ?boolean) => {};
let updateMessage = (message: string) => {};
var done = (result : ?boolean) => {};
var updateMessage = (message : string ) => {};
function runTestCase(description: string, fn) {
function runTestCase(description : string, fn) {
updateMessage(description);
fn();
}
function expectTrue(condition: boolean, message: string) {
function expectTrue(condition : boolean, message : string) {
if (!condition) {
throw new Error(message);
}
}
function expectEqual(lhs, rhs, testname: string) {
function expectEqual(lhs, rhs, testname : string) {
expectTrue(
!deepDiffer(lhs, rhs),
'Error in test ' +
testname +
': expected\n' +
JSON.stringify(rhs) +
'\ngot\n' +
JSON.stringify(lhs),
'Error in test ' + testname + ': expected\n' + JSON.stringify(rhs) +
'\ngot\n' + JSON.stringify(lhs)
);
}
@@ -59,14 +61,11 @@ function expectAsyncNoError(place, err) {
if (err instanceof Error) {
err = err.message;
}
expectTrue(
err === null,
'Unexpected error in ' + place + ': ' + JSON.stringify(err),
);
expectTrue(err === null, 'Unexpected error in ' + place + ': ' + JSON.stringify(err));
}
function testSetAndGet() {
AsyncStorage.setItem(KEY_1, VAL_1, err1 => {
AsyncStorage.setItem(KEY_1, VAL_1, (err1) => {
expectAsyncNoError('testSetAndGet/setItem', err1);
AsyncStorage.getItem(KEY_1, (err2, result) => {
expectAsyncNoError('testSetAndGet/getItem', err2);
@@ -87,8 +86,8 @@ function testMissingGet() {
}
function testSetTwice() {
AsyncStorage.setItem(KEY_1, VAL_1, () => {
AsyncStorage.setItem(KEY_1, VAL_1, () => {
AsyncStorage.setItem(KEY_1, VAL_1, ()=>{
AsyncStorage.setItem(KEY_1, VAL_1, ()=>{
AsyncStorage.getItem(KEY_1, (err, result) => {
expectAsyncNoError('testSetTwice/setItem', err);
expectEqual(result, VAL_1, 'testSetTwice');
@@ -100,16 +99,16 @@ function testSetTwice() {
}
function testRemoveItem() {
AsyncStorage.setItem(KEY_1, VAL_1, () => {
AsyncStorage.setItem(KEY_2, VAL_2, () => {
AsyncStorage.setItem(KEY_1, VAL_1, ()=>{
AsyncStorage.setItem(KEY_2, VAL_2, ()=>{
AsyncStorage.getAllKeys((err, result) => {
expectAsyncNoError('testRemoveItem/getAllKeys', err);
expectTrue(
result.indexOf(KEY_1) >= 0 && result.indexOf(KEY_2) >= 0,
'Missing KEY_1 or KEY_2 in ' + '(' + result + ')',
'Missing KEY_1 or KEY_2 in ' + '(' + result + ')'
);
updateMessage('testRemoveItem - add two items');
AsyncStorage.removeItem(KEY_1, err2 => {
AsyncStorage.removeItem(KEY_1, (err2) => {
expectAsyncNoError('testRemoveItem/removeItem', err2);
updateMessage('delete successful ');
AsyncStorage.getItem(KEY_1, (err3, result2) => {
@@ -117,17 +116,17 @@ function testRemoveItem() {
expectEqual(
result2,
null,
'testRemoveItem: key_1 present after delete',
'testRemoveItem: key_1 present after delete'
);
updateMessage('key properly removed ');
AsyncStorage.getAllKeys((err4, result3) => {
expectAsyncNoError('testRemoveItem/getAllKeys', err4);
expectTrue(
result3.indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + result3,
);
updateMessage('proper length returned.');
runTestCase('should merge values', testMerge);
expectAsyncNoError('testRemoveItem/getAllKeys', err4);
expectTrue(
result3.indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + result3
);
updateMessage('proper length returned.');
runTestCase('should merge values', testMerge);
});
});
});
@@ -137,9 +136,9 @@ function testRemoveItem() {
}
function testMerge() {
AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), err1 => {
AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), (err1) => {
expectAsyncNoError('testMerge/setItem', err1);
AsyncStorage.mergeItem(KEY_MERGE, JSON.stringify(VAL_MERGE_2), err2 => {
AsyncStorage.mergeItem(KEY_MERGE, JSON.stringify(VAL_MERGE_2), (err2) => {
expectAsyncNoError('testMerge/mergeItem', err2);
AsyncStorage.getItem(KEY_MERGE, (err3, result) => {
expectAsyncNoError('testMerge/setItem', err3);
@@ -152,54 +151,45 @@ function testMerge() {
}
function testOptimizedMultiGet() {
let batch = [
[KEY_1, VAL_1],
[KEY_2, VAL_2],
];
let batch = [[KEY_1, VAL_1], [KEY_2, VAL_2]];
let keys = batch.map(([key, value]) => key);
AsyncStorage.multiSet(batch, err1 => {
AsyncStorage.multiSet(batch, (err1) => {
// yes, twice on purpose
[1, 2].forEach(i => {
;[1, 2].forEach((i) => {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1);
AsyncStorage.multiGet(keys, (err2, result) => {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2);
expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`);
updateMessage(
'multiGet([key_1, key_2]) correctly returned ' +
JSON.stringify(result),
);
updateMessage('multiGet([key_1, key_2]) correctly returned ' + JSON.stringify(result));
done();
});
});
});
}
class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> {
state: any | {|done: boolean, messages: string|} = {
class AsyncStorageTest extends React.Component<{}, $FlowFixMeState> {
state = {
messages: 'Initializing...',
done: false,
};
componentDidMount() {
done = () =>
this.setState({done: true}, () => {
TestModule.markTestCompleted();
});
updateMessage = msg => {
done = () => this.setState({done: true}, () => {
TestModule.markTestCompleted();
});
updateMessage = (msg) => {
this.setState({messages: this.state.messages.concat('\n' + msg)});
DEBUG && console.log(msg);
};
AsyncStorage.clear(testSetAndGet);
}
render(): React.Node {
render() {
return (
<View style={styles.container}>
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error found when Flow v0.54 was deployed.
* To see the error delete this comment and run Flow. */
this.constructor.displayName + ': '}
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
{'\n\n' + this.state.messages}
</Text>
@@ -208,13 +198,6 @@ class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> {
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
padding: 40,
},
});
AsyncStorageTest.displayName = 'AsyncStorageTest';
module.exports = AsyncStorageTest;
@@ -1,87 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
'use strict';
import type {ExtendedError} from 'react-native/Libraries/Core/Devtools/parseErrorStack';
const React = require('react');
const ReactNative = require('react-native');
const parseErrorStack = require('react-native/Libraries/Core/Devtools/parseErrorStack');
const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
componentDidMount() {
if (typeof global.globalEvalWithSourceUrl !== 'function') {
throw new Error(
'Expected to find globalEvalWithSourceUrl function on global object but found ' +
typeof global.globalEvalWithSourceUrl,
);
}
const value = global.globalEvalWithSourceUrl('42');
if (value !== 42) {
throw new Error(
'Expected globalEvalWithSourceUrl(expression) to return a value',
);
}
let syntaxError: ?ExtendedError;
try {
global.globalEvalWithSourceUrl('{');
} catch (e) {
syntaxError = e;
}
if (!syntaxError) {
throw new Error(
'Expected globalEvalWithSourceUrl to throw on a syntax error',
);
}
// Hermes throws an Error instead of a SyntaxError
// https://github.com/facebook/hermes/issues/400
if (
syntaxError.jsEngine !== 'hermes' &&
!(syntaxError instanceof SyntaxError)
) {
throw new Error(
'Expected globalEvalWithSourceUrl to throw SyntaxError on a syntax error',
);
}
const url = 'http://example.com/foo.js';
let error;
try {
global.globalEvalWithSourceUrl('throw new Error()', url);
} catch (e) {
error = e;
}
if (!error) {
throw new Error(
'Expected globalEvalWithSourceUrl to throw an Error object',
);
}
const parsedStack = parseErrorStack(error?.stack);
if (parsedStack[0].file !== url) {
throw new Error(
`Expected first eval stack frame to be in ${url} but found ${String(
parsedStack[0].file,
)}`,
);
}
TestModule.markTestCompleted();
}
render(): React.Node {
return <View />;
}
}
GlobalEvalWithSourceUrlTest.displayName = 'GlobalEvalWithSourceUrlTest';
module.exports = GlobalEvalWithSourceUrlTest;
+43 -52
View File
@@ -1,19 +1,25 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow strict-local
* @flow
* @providesModule ImageCachePolicyTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {Image, View, Text, StyleSheet} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var ReactNative = require('react-native');
var {
Image,
View,
Text,
StyleSheet,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
/*
* The reload and force-cache tests don't actually verify that the complete functionality.
@@ -27,26 +33,22 @@ const {TestModule} = ReactNative.NativeModules;
const TESTS = ['only-if-cached', 'default', 'reload', 'force-cache'];
type Props = {...};
type Props = {}
type State = {
'only-if-cached'?: boolean,
default?: boolean,
reload?: boolean,
'default'?: boolean,
'reload'?: boolean,
'force-cache'?: boolean,
...
};
}
class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
state: $FlowFixMe | {...} = {};
state = {}
shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
shouldComponentUpdate(nextProps: Props, nextState: State) {
const results: Array<?boolean> = TESTS.map(x => nextState[x]);
if (!results.includes(undefined)) {
const result: boolean = results.reduce(
(x, y) => (x === y) === true,
true,
);
const result: boolean = results.reduce((x,y) => x === y === true, true)
TestModule.markTestPassed(result);
}
@@ -57,50 +59,42 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
this.setState({[name]: pass});
}
render(): React.Node {
render() {
return (
<View style={styles.container}>
<View style={{flex: 1}}>
<Text>Hello</Text>
<Image
source={{
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'only-if-cached'
}}
onLoad={() => this.testComplete('only-if-cached', false)}
onError={() => this.testComplete('only-if-cached', true)}
style={styles.base}
/>
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/master/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
Date.now(),
cache: 'only-if-cached',
}}
onLoad={() => this.testComplete('only-if-cached', false)}
onError={() => this.testComplete('only-if-cached', true)}
style={styles.base}
/>
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/master/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
Date.now(),
cache: 'default',
}}
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'default'
}}
onLoad={() => this.testComplete('default', true)}
onError={() => this.testComplete('default', false)}
style={styles.base}
/>
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/master/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
Date.now(),
cache: 'reload',
}}
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'reload'
}}
onLoad={() => this.testComplete('reload', true)}
onError={() => this.testComplete('reload', false)}
style={styles.base}
/>
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/master/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
Date.now(),
cache: 'force-cache',
}}
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'force-cache'
}}
onLoad={() => this.testComplete('force-cache', true)}
onError={() => this.testComplete('force-cache', false)}
style={styles.base}
@@ -111,9 +105,6 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
base: {
width: 100,
height: 100,
+18 -15
View File
@@ -1,38 +1,41 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow strict-local
* @flow
* @providesModule ImageSnapshotTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {Image} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var ReactNative = require('react-native');
var {
Image,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
class ImageSnapshotTest extends React.Component<{...}> {
class ImageSnapshotTest extends React.Component<{}> {
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
}
}
done: (success: boolean) => void = (success: boolean) => {
done = (success : boolean) => {
TestModule.markTestPassed(success);
};
render(): React.Node {
render() {
return (
<Image
source={require('./blue_square.png')}
defaultSource={require('./red_square.png')}
onLoad={() => TestModule.verifySnapshot(this.done)}
/>
onLoad={() => TestModule.verifySnapshot(this.done)} />
);
}
}
+26 -32
View File
@@ -1,32 +1,36 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow
* @providesModule IntegrationTestHarnessTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
var requestAnimationFrame = require('fbjs/lib/requestAnimationFrame');
var React = require('react');
var PropTypes = require('prop-types');
var ReactNative = require('react-native');
var {
Text,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
const {Text, View, StyleSheet} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
type Props = $ReadOnly<{|
class IntegrationTestHarnessTest extends React.Component<{
shouldThrow?: boolean,
waitOneFrame?: boolean,
|}>;
}, $FlowFixMeState> {
static propTypes = {
shouldThrow: PropTypes.bool,
waitOneFrame: PropTypes.bool,
};
type State = {|
done: boolean,
|};
class IntegrationTestHarnessTest extends React.Component<Props, State> {
state: State = {
state = {
done: false,
};
@@ -38,7 +42,7 @@ class IntegrationTestHarnessTest extends React.Component<Props, State> {
}
}
runTest: () => void = () => {
runTest = () => {
if (this.props.shouldThrow) {
throw new Error('Throwing error because shouldThrow');
}
@@ -52,14 +56,11 @@ class IntegrationTestHarnessTest extends React.Component<Props, State> {
});
};
render(): React.Node {
render() {
return (
<View style={styles.container}>
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error found when Flow v0.54 was deployed.
* To see the error delete this comment and run Flow. */
this.constructor.displayName + ': '}
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
@@ -67,13 +68,6 @@ class IntegrationTestHarnessTest extends React.Component<Props, State> {
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
padding: 40,
},
});
IntegrationTestHarnessTest.displayName = 'IntegrationTestHarnessTest';
module.exports = IntegrationTestHarnessTest;
+23 -27
View File
@@ -1,19 +1,19 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow
* @providesModule IntegrationTestsApp
*/
'use strict';
require('react-native/Libraries/Core/InitializeCore');
const React = require('react');
const ReactNative = require('react-native');
const {
var React = require('react');
var ReactNative = require('react-native');
var {
AppRegistry,
ScrollView,
StyleSheet,
@@ -23,7 +23,7 @@ const {
} = ReactNative;
// Keep this list in sync with RNTesterIntegrationTests.m
const TESTS = [
var TESTS = [
require('./IntegrationTestHarnessTest'),
require('./TimersTest'),
require('./AsyncStorageTest'),
@@ -33,25 +33,22 @@ const TESTS = [
require('./ImageCachePolicyTest'),
require('./ImageSnapshotTest'),
require('./PromiseTest'),
require('./WebViewTest'),
require('./SyncMethodTest'),
require('./WebSocketTest'),
require('./AccessibilityManagerTest'),
require('./GlobalEvalWithSourceUrlTest'),
];
TESTS.forEach(
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This comment
* suppresses an error found when Flow v0.54 was deployed. To see the error
* delete this comment and run Flow. */
test => AppRegistry.registerComponent(test.displayName, () => test),
(test) => AppRegistry.registerComponent(test.displayName, () => test)
);
// Modules required for integration tests
require('./LoggingTestModule');
require('LoggingTestModule');
type Test = any;
class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
class IntegrationTestsApp extends React.Component<{}, $FlowFixMeState> {
state = {
test: (null: ?Test),
};
@@ -61,8 +58,8 @@ class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
return (
<ScrollView>
{/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error when upgrading Flow's support for
* React. To see the error delete this comment and run Flow. */}
* comment suppresses an error when upgrading Flow's support for
* React. To see the error delete this comment and run Flow. */}
<this.state.test />
</ScrollView>
);
@@ -71,21 +68,20 @@ class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
<View style={styles.container}>
<Text style={styles.row}>
Click on a test to run it in this shell for easier debugging and
development. Run all tests in the testing environment with cmd+U in
development. Run all tests in the testing environment with cmd+U in
Xcode.
</Text>
<View style={styles.separator} />
<ScrollView>
{TESTS.map(test => [
{TESTS.map((test) => [
<TouchableOpacity
onPress={() => this.setState({test})}
/* $FlowFixMe(>=0.115.0 site=react_native_fb) This comment
* suppresses an error found when Flow v0.115 was deployed. To
* see the error, delete this comment and run Flow. */
style={styles.row}>
<Text style={styles.testName}>{test.displayName}</Text>
<Text style={styles.testName}>
{test.displayName}
</Text>
</TouchableOpacity>,
<View style={styles.separator} />,
<View style={styles.separator} />
])}
</ScrollView>
</View>
@@ -93,7 +89,7 @@ class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
}
}
const styles = StyleSheet.create({
var styles = StyleSheet.create({
container: {
backgroundColor: 'white',
marginTop: 40,
+93 -103
View File
@@ -1,34 +1,53 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow strict-local
* @providesModule LayoutEventsTest
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {Image, LayoutAnimation, StyleSheet, Text, View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var {
Image,
LayoutAnimation,
StyleSheet,
Text,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
var deepDiffer = require('deepDiffer');
function debug(...args) {
// console.log.apply(null, arguments);
}
import type {
Layout,
LayoutEvent,
} from 'react-native/Libraries/Types/CoreEventTypes';
type Props = $ReadOnly<{||}>;
type Layout = {
x: number;
y: number;
width: number;
height: number;
};
type LayoutEvent = {
nativeEvent: {
layout: Layout;
};
};
type Style = {
margin?: number,
padding?: number,
borderColor?: string,
borderWidth?: number,
backgroundColor?: string,
width?: number,
};
type State = {
didAnimation: boolean,
@@ -36,59 +55,48 @@ type State = {
imageLayout?: Layout,
textLayout?: Layout,
viewLayout?: Layout,
viewStyle?: ViewStyleProp,
containerStyle?: ViewStyleProp,
...
viewStyle?: Style,
containerStyle?: Style,
};
class LayoutEventsTest extends React.Component<Props, State> {
_view: ?React.ElementRef<typeof View>;
_img: ?React.ElementRef<typeof Image>;
_txt: ?React.ElementRef<typeof Text>;
state: State = {
didAnimation: false,
};
animateViewLayout() {
var LayoutEventsTest = createReactClass({
displayName: 'LayoutEventsTest',
getInitialState(): State {
return {
didAnimation: false,
};
},
animateViewLayout: function() {
debug('animateViewLayout invoked');
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring, () => {
debug('animateViewLayout done');
this.checkLayout(this.addWrapText);
});
LayoutAnimation.configureNext(
LayoutAnimation.Presets.spring,
() => {
debug('animateViewLayout done');
this.checkLayout(this.addWrapText);
}
);
this.setState({viewStyle: {margin: 60}});
}
addWrapText: () => void = () => {
},
addWrapText: function() {
debug('addWrapText invoked');
this.setState(
{extraText: ' And a bunch more text to wrap around a few lines.'},
() => this.checkLayout(this.changeContainer),
() => this.checkLayout(this.changeContainer)
);
};
changeContainer: () => void = () => {
},
changeContainer: function() {
debug('changeContainer invoked');
this.setState({containerStyle: {width: 280}}, () =>
this.checkLayout(TestModule.markTestCompleted),
this.setState(
{containerStyle: {width: 280}},
() => this.checkLayout(TestModule.markTestCompleted)
);
};
checkLayout: (next?: ?() => void) => void = (next?: ?() => void) => {
const view = this._view;
const txt = this._txt;
const img = this._img;
if (view == null || txt == null || img == null) {
},
checkLayout: function(next?: ?Function) {
if (!this.isMounted()) {
return;
}
view.measure((x, y, width, height) => {
this.compare(
'view',
{x, y, width, height},
this.state.viewLayout || null,
);
this.refs.view.measure((x, y, width, height) => {
this.compare('view', {x, y, width, height}, this.state.viewLayout);
if (typeof next === 'function') {
next();
} else if (!this.state.didAnimation) {
@@ -97,69 +105,49 @@ class LayoutEventsTest extends React.Component<Props, State> {
this.state.didAnimation = true;
}
});
txt.measure((x, y, width, height) => {
this.refs.txt.measure((x, y, width, height) => {
this.compare('txt', {x, y, width, height}, this.state.textLayout);
});
img.measure((x, y, width, height) => {
this.refs.img.measure((x, y, width, height) => {
this.compare('img', {x, y, width, height}, this.state.imageLayout);
});
};
compare(node: string, measured: Layout, onLayout?: ?Layout): void {
},
compare: function(node: string, measured: any, onLayout: any): void {
if (deepDiffer(measured, onLayout)) {
const data = {measured, onLayout};
var data = {measured, onLayout};
throw new Error(
node +
' onLayout mismatch with measure ' +
JSON.stringify(data, null, ' '),
node + ' onLayout mismatch with measure ' +
JSON.stringify(data, null, ' ')
);
}
}
onViewLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
},
onViewLayout: function(e: LayoutEvent) {
debug('received view layout event\n', e.nativeEvent);
this.setState({viewLayout: e.nativeEvent.layout}, this.checkLayout);
};
onTextLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
},
onTextLayout: function(e: LayoutEvent) {
debug('received text layout event\n', e.nativeEvent);
this.setState({textLayout: e.nativeEvent.layout}, this.checkLayout);
};
onImageLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
},
onImageLayout: function(e: LayoutEvent) {
debug('received image layout event\n', e.nativeEvent);
this.setState({imageLayout: e.nativeEvent.layout}, this.checkLayout);
};
render(): React.Node {
const viewStyle = [styles.view, this.state.viewStyle];
const textLayout = this.state.textLayout || {width: '?', height: '?'};
const imageLayout = this.state.imageLayout || {x: '?', y: '?'};
},
render: function() {
var viewStyle = [styles.view, this.state.viewStyle];
var textLayout = this.state.textLayout || {width: '?', height: '?'};
var imageLayout = this.state.imageLayout || {x: '?', y: '?'};
debug('viewLayout', this.state.viewLayout);
return (
<View style={[styles.container, this.state.containerStyle]}>
<View
ref={ref => {
this._view = ref;
}}
onLayout={this.onViewLayout}
style={viewStyle}>
<View ref="view" onLayout={this.onViewLayout} style={viewStyle}>
<Image
ref={ref => {
this._img = ref;
}}
ref="img"
onLayout={this.onImageLayout}
style={styles.image}
source={{uri: 'uie_thumb_big.png'}}
/>
<Text
ref={ref => {
this._txt = ref;
}}
onLayout={this.onTextLayout}
style={styles.text}>
<Text ref="txt" onLayout={this.onTextLayout} style={styles.text}>
A simple piece of text.{this.state.extraText}
</Text>
<Text>
@@ -171,9 +159,9 @@ class LayoutEventsTest extends React.Component<Props, State> {
</View>
);
}
}
});
const styles = StyleSheet.create({
var styles = StyleSheet.create({
container: {
margin: 40,
},
@@ -197,4 +185,6 @@ const styles = StyleSheet.create({
},
});
LayoutEventsTest.displayName = 'LayoutEventsTest';
module.exports = LayoutEventsTest;
+17 -12
View File
@@ -1,29 +1,31 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @providesModule LoggingTestModule
*/
'use strict';
const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
var BatchedBridge = require('BatchedBridge');
const invariant = require('invariant');
var warning = require('fbjs/lib/warning');
var invariant = require('fbjs/lib/invariant');
const LoggingTestModule = {
var LoggingTestModule = {
logToConsole: function(str) {
console.log(str);
},
logToConsoleAfterWait: function(str, timeout_ms) {
logToConsoleAfterWait: function(str,timeout_ms) {
setTimeout(function() {
console.log(str);
}, timeout_ms);
},
warning: function(str) {
console.warn(str);
warning(false, str);
},
invariant: function(str) {
invariant(false, str);
@@ -33,9 +35,12 @@ const LoggingTestModule = {
},
throwError: function(str) {
throw new Error(str);
},
}
};
BatchedBridge.registerCallableModule('LoggingTestModule', LoggingTestModule);
BatchedBridge.registerCallableModule(
'LoggingTestModule',
LoggingTestModule
);
module.exports = LoggingTestModule;
+31 -32
View File
@@ -1,25 +1,26 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow
* @providesModule PromiseTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var ReactNative = require('react-native');
var { View } = ReactNative;
var { TestModule } = ReactNative.NativeModules;
class PromiseTest extends React.Component<{...}> {
shouldResolve: boolean = false;
shouldReject: boolean = false;
shouldSucceedAsync: boolean = false;
shouldThrowAsync: boolean = false;
class PromiseTest extends React.Component<{}> {
shouldResolve = false;
shouldReject = false;
shouldSucceedAsync = false;
shouldThrowAsync = false;
componentDidMount() {
Promise.all([
@@ -27,29 +28,27 @@ class PromiseTest extends React.Component<{...}> {
this.testShouldReject(),
this.testShouldSucceedAsync(),
this.testShouldThrowAsync(),
]).then(() =>
TestModule.markTestPassed(
this.shouldResolve &&
this.shouldReject &&
this.shouldSucceedAsync &&
this.shouldThrowAsync,
),
);
]).then(() => TestModule.markTestPassed(
this.shouldResolve && this.shouldReject &&
this.shouldSucceedAsync && this.shouldThrowAsync
));
}
testShouldResolve: () => any = () => {
return TestModule.shouldResolve()
.then(() => (this.shouldResolve = true))
.catch(() => (this.shouldResolve = false));
testShouldResolve = () => {
return TestModule
.shouldResolve()
.then(() => this.shouldResolve = true)
.catch(() => this.shouldResolve = false);
};
testShouldReject: () => any = () => {
return TestModule.shouldReject()
.then(() => (this.shouldReject = false))
.catch(() => (this.shouldReject = true));
testShouldReject = () => {
return TestModule
.shouldReject()
.then(() => this.shouldReject = false)
.catch(() => this.shouldReject = true);
};
testShouldSucceedAsync: () => Promise<any> = async (): Promise<any> => {
testShouldSucceedAsync = async (): Promise<any> => {
try {
await TestModule.shouldResolve();
this.shouldSucceedAsync = true;
@@ -58,7 +57,7 @@ class PromiseTest extends React.Component<{...}> {
}
};
testShouldThrowAsync: () => Promise<any> = async (): Promise<any> => {
testShouldThrowAsync = async (): Promise<any> => {
try {
await TestModule.shouldReject();
this.shouldThrowAsync = false;
+15 -11
View File
@@ -1,26 +1,30 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* @providesModule PropertiesUpdateTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
var React = require('react');
var ReactNative = require('react-native');
var {
View,
} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var { TestModule } = ReactNative.NativeModules;
class PropertiesUpdateTest extends React.Component {
render() {
if (this.props.markTestPassed) {
TestModule.markTestPassed(true);
}
return <View />;
return (
<View/>
);
}
}
@@ -1,18 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @providesModule RCTRootViewIntegrationTestApp
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
require('regenerator-runtime/runtime');
const {
var React = require('react');
var ReactNative = require('react-native');
var {
AppRegistry,
ScrollView,
StyleSheet,
@@ -22,14 +25,14 @@ const {
} = ReactNative;
/* Keep this list in sync with RCTRootViewIntegrationTests.m */
const TESTS = [
var TESTS = [
require('./PropertiesUpdateTest'),
require('./ReactContentSizeUpdateTest'),
require('./SizeFlexibilityUpdateTest'),
];
TESTS.forEach(test =>
AppRegistry.registerComponent(test.displayName, () => test),
TESTS.forEach(
(test) => AppRegistry.registerComponent(test.displayName, () => test)
);
class RCTRootViewIntegrationTestApp extends React.Component {
@@ -49,18 +52,20 @@ class RCTRootViewIntegrationTestApp extends React.Component {
<View style={styles.container}>
<Text style={styles.row}>
Click on a test to run it in this shell for easier debugging and
development. Run all tests in the testing environment with cmd+U in
development. Run all tests in the testing environment with cmd+U in
Xcode.
</Text>
<View style={styles.separator} />
<ScrollView>
{TESTS.map(test => [
{TESTS.map((test) => [
<TouchableOpacity
onPress={() => this.setState({test})}
style={styles.row}>
<Text style={styles.testName}>{test.displayName}</Text>
<Text style={styles.testName}>
{test.displayName}
</Text>
</TouchableOpacity>,
<View style={styles.separator} />,
<View style={styles.separator} />
])}
</ScrollView>
</View>
@@ -68,7 +73,7 @@ class RCTRootViewIntegrationTestApp extends React.Component {
}
}
const styles = StyleSheet.create({
var styles = StyleSheet.create({
container: {
backgroundColor: 'white',
marginTop: 40,
@@ -86,7 +91,4 @@ const styles = StyleSheet.create({
},
});
AppRegistry.registerComponent(
'RCTRootViewIntegrationTestApp',
() => RCTRootViewIntegrationTestApp,
);
AppRegistry.registerComponent('RCTRootViewIntegrationTestApp', () => RCTRootViewIntegrationTestApp);
+50 -64
View File
@@ -1,91 +1,77 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* @providesModule ReactContentSizeUpdateTest
*/
'use strict';
const RCTNativeAppEventEmitter = require('react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter');
const React = require('react');
const ReactNative = require('react-native');
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var RCTNativeAppEventEmitter = require('RCTNativeAppEventEmitter');
var Subscribable = require('Subscribable');
var TimerMixin = require('react-timer-mixin');
const {View} = ReactNative;
var { View } = ReactNative;
const {TestModule} = ReactNative.NativeModules;
import {type EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
var { TestModule } = ReactNative.NativeModules;
const reactViewWidth = 101;
const reactViewHeight = 102;
const newReactViewWidth = 201;
const newReactViewHeight = 202;
var reactViewWidth = 101;
var reactViewHeight = 102;
var newReactViewWidth = 201;
var newReactViewHeight = 202;
type Props = {||};
var ReactContentSizeUpdateTest = createReactClass({
displayName: 'ReactContentSizeUpdateTest',
mixins: [Subscribable.Mixin,
TimerMixin],
type State = {|
height: number,
width: number,
|};
class ReactContentSizeUpdateTest extends React.Component<Props, State> {
_timeoutID: ?TimeoutID = null;
_subscription: ?EventSubscription = null;
state: State = {
height: reactViewHeight,
width: reactViewWidth,
};
UNSAFE_componentWillMount() {
this._subscription = RCTNativeAppEventEmitter.addListener(
componentWillMount: function() {
this.addListenerOn(
RCTNativeAppEventEmitter,
'rootViewDidChangeIntrinsicSize',
this.rootViewDidChangeIntrinsicSize,
this.rootViewDidChangeIntrinsicSize
);
}
},
componentDidMount() {
this._timeoutID = setTimeout(() => {
this.updateViewSize();
}, 1000);
}
getInitialState: function() {
return {
height: reactViewHeight,
width: reactViewWidth,
};
},
componentWillUnmount() {
if (this._timeoutID != null) {
clearTimeout(this._timeoutID);
}
if (this._subscription != null) {
this._subscription.remove();
}
}
updateViewSize() {
updateViewSize: function() {
this.setState({
height: newReactViewHeight,
width: newReactViewWidth,
});
}
},
rootViewDidChangeIntrinsicSize: (intrinsicSize: State) => void = (
intrinsicSize: State,
) => {
if (
intrinsicSize.height === newReactViewHeight &&
intrinsicSize.width === newReactViewWidth
) {
componentDidMount: function() {
this.setTimeout(
() => { this.updateViewSize(); },
1000
);
},
rootViewDidChangeIntrinsicSize: function(intrinsicSize) {
if (intrinsicSize.height === newReactViewHeight && intrinsicSize.width === newReactViewWidth) {
TestModule.markTestPassed(true);
}
};
},
render(): React.Node {
render() {
return (
<View style={{height: this.state.height, width: this.state.width}} />
<View style={{'height':this.state.height, 'width':this.state.width}}/>
);
}
}
});
ReactContentSizeUpdateTest.displayName = 'ReactContentSizeUpdateTest';
module.exports = ReactContentSizeUpdateTest;
+20 -19
View File
@@ -1,22 +1,27 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow strict-local
* @flow
* @providesModule SimpleSnapshotTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
var React = require('react');
var ReactNative = require('react-native');
var requestAnimationFrame = require('fbjs/lib/requestAnimationFrame');
const {StyleSheet, View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var {
StyleSheet,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
class SimpleSnapshotTest extends React.Component<{...}> {
class SimpleSnapshotTest extends React.Component<{}> {
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
@@ -24,13 +29,13 @@ class SimpleSnapshotTest extends React.Component<{...}> {
requestAnimationFrame(() => TestModule.verifySnapshot(this.done));
}
done: (success: boolean) => void = (success: boolean) => {
done = (success : boolean) => {
TestModule.markTestPassed(success);
};
render(): React.Node {
render() {
return (
<View style={styles.container}>
<View style={{backgroundColor: 'white', padding: 100}}>
<View style={styles.box1} />
<View style={styles.box2} />
</View>
@@ -38,11 +43,7 @@ class SimpleSnapshotTest extends React.Component<{...}> {
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
padding: 100,
},
var styles = StyleSheet.create({
box1: {
width: 80,
height: 50,
+40 -62
View File
@@ -1,61 +1,47 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* @providesModule SizeFlexibilityUpdateTest
*/
'use strict';
const RCTNativeAppEventEmitter = require('react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter');
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var RCTNativeAppEventEmitter = require('RCTNativeAppEventEmitter');
var Subscribable = require('Subscribable');
var { View } = ReactNative;
const {TestModule} = ReactNative.NativeModules;
import {type EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
var { TestModule } = ReactNative.NativeModules;
const reactViewWidth = 111;
const reactViewHeight = 222;
var reactViewWidth = 111;
var reactViewHeight = 222;
let finalState = false;
var finalState = false;
type Props = $ReadOnly<{|
width: boolean,
height: boolean,
both: boolean,
none: boolean,
|}>;
var SizeFlexibilityUpdateTest = createReactClass({
displayName: 'SizeFlexibilityUpdateTest',
mixins: [Subscribable.Mixin],
class SizeFlexibilityUpdateTest extends React.Component<Props> {
_subscription: ?EventSubscription = null;
UNSAFE_componentWillMount() {
this._subscription = RCTNativeAppEventEmitter.addListener(
componentWillMount: function() {
this.addListenerOn(
RCTNativeAppEventEmitter,
'rootViewDidChangeIntrinsicSize',
this.rootViewDidChangeIntrinsicSize,
this.rootViewDidChangeIntrinsicSize
);
}
},
componentWillUnmount() {
if (this._subscription != null) {
this._subscription.remove();
}
}
markPassed: () => void = () => {
markPassed: function() {
TestModule.markTestPassed(true);
finalState = true;
};
},
rootViewDidChangeIntrinsicSize: function(intrinsicSize) {
rootViewDidChangeIntrinsicSize: (intrinsicSize: {
height: number,
width: number,
...
}) => void = (intrinsicSize: {width: number, height: number, ...}) => {
if (finalState) {
// If a test reaches its final state, it is not expected to do anything more
TestModule.markTestPassed(false);
@@ -63,46 +49,38 @@ class SizeFlexibilityUpdateTest extends React.Component<Props> {
}
if (this.props.both) {
if (
intrinsicSize.width === reactViewWidth &&
intrinsicSize.height === reactViewHeight
) {
if (intrinsicSize.width === reactViewWidth && intrinsicSize.height === reactViewHeight) {
this.markPassed();
return;
}
}
if (this.props.height) {
if (
intrinsicSize.width !== reactViewWidth &&
intrinsicSize.height === reactViewHeight
) {
if (intrinsicSize.width !== reactViewWidth && intrinsicSize.height === reactViewHeight) {
this.markPassed();
return;
}
}
if (this.props.width) {
if (
intrinsicSize.width === reactViewWidth &&
intrinsicSize.height !== reactViewHeight
) {
if (intrinsicSize.width === reactViewWidth && intrinsicSize.height !== reactViewHeight) {
this.markPassed();
return;
}
}
if (this.props.none) {
if (
intrinsicSize.width !== reactViewWidth &&
intrinsicSize.height !== reactViewHeight
) {
if (intrinsicSize.width !== reactViewWidth && intrinsicSize.height !== reactViewHeight) {
this.markPassed();
return;
}
}
};
},
render(): React.Node {
return <View style={{height: reactViewHeight, width: reactViewWidth}} />;
render() {
return (
<View style={{'height':reactViewHeight, 'width':reactViewWidth}}/>
);
}
}
});
SizeFlexibilityUpdateTest.displayName = 'SizeFlexibilityUpdateTest';
module.exports = SizeFlexibilityUpdateTest;
+20 -31
View File
@@ -1,46 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow strict-local
* @flow
* @providesModule SyncMethodTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
var React = require('react');
var ReactNative = require('react-native');
var { View } = ReactNative;
const {TestModule, RNTesterTestModule} = ReactNative.NativeModules;
const {
TestModule,
RNTesterTestModule,
} = ReactNative.NativeModules;
class SyncMethodTest extends React.Component<{...}> {
class SyncMethodTest extends React.Component<{}> {
componentDidMount() {
if (
RNTesterTestModule.echoString('test string value') !== 'test string value'
) {
throw new Error('Something wrong with echoString sync method');
if (RNTesterTestModule.echoString('test string value') !== 'test string value') {
throw new Error('Something wrong with sync method export');
}
if (RNTesterTestModule.methodThatReturnsNil() != null) {
throw new Error('Something wrong with methodThatReturnsNil sync method');
throw new Error('Something wrong with sync method export');
}
let response;
RNTesterTestModule.methodThatCallsCallbackWithString('test', echo => {
response = echo;
});
requestAnimationFrame(() => {
if (response === 'test') {
TestModule.markTestCompleted();
} else {
throw new Error(
'Something wrong with methodThatCallsCallbackWithString sync method, ' +
'got response ' +
JSON.stringify(response),
);
}
});
TestModule.markTestCompleted();
}
render(): React.Node {
+69 -170
View File
@@ -1,270 +1,169 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow
* @providesModule TimersTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {StyleSheet, Text, View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var TimerMixin = require('react-timer-mixin');
type Props = $ReadOnly<{||}>;
var {
StyleSheet,
Text,
View,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
type State = {|
count: number,
done: boolean,
|};
var TimersTest = createReactClass({
displayName: 'TimersTest',
mixins: [TimerMixin],
type ImmediateID = Object;
_nextTest: () => {},
_interval: -1,
class TimersTest extends React.Component<Props, State> {
_nextTest = () => {};
_interval: ?IntervalID = null;
_timeoutIDs: Set<TimeoutID> = new Set();
_intervalIDs: Set<IntervalID> = new Set();
_immediateIDs: Set<ImmediateID> = new Set();
_animationFrameIDs: Set<AnimationFrameID> = new Set();
state: State = {
count: 0,
done: false,
};
setTimeout(fn: () => void, time: number): TimeoutID {
const id = setTimeout(() => {
this._timeoutIDs.delete(id);
fn();
}, time);
this._timeoutIDs.add(id);
return id;
}
clearTimeout(id: TimeoutID) {
this._timeoutIDs.delete(id);
clearTimeout(id);
}
setInterval(fn: () => void, time: number): IntervalID {
const id = setInterval(() => {
fn();
}, time);
this._intervalIDs.add(id);
return id;
}
clearInterval(id: IntervalID) {
this._intervalIDs.delete(id);
clearInterval(id);
}
setImmediate(fn: () => void): ImmediateID {
const id = setImmediate(() => {
this._immediateIDs.delete(id);
fn();
});
this._immediateIDs.add(id);
return id;
}
requestAnimationFrame(fn: () => void): AnimationFrameID {
const id = requestAnimationFrame(() => {
this._animationFrameIDs.delete(id);
fn();
});
this._animationFrameIDs.add(id);
return id;
}
cancelAnimationFrame(id: AnimationFrameID): void {
this._animationFrameIDs.delete(id);
cancelAnimationFrame(id);
}
getInitialState() {
return {
count: 0,
done: false,
};
},
componentDidMount() {
this.setTimeout(this.testSetTimeout0, 1000);
}
},
testSetTimeout0() {
this.setTimeout(this.testSetTimeout1, 0);
}
},
testSetTimeout1() {
this.setTimeout(this.testSetTimeout50, 1);
}
},
testSetTimeout50() {
this.setTimeout(this.testRequestAnimationFrame, 50);
}
},
testRequestAnimationFrame() {
this.requestAnimationFrame(this.testSetInterval0);
}
},
testSetInterval0() {
this._nextTest = this.testSetInterval20;
this._interval = this.setInterval(this._incrementInterval, 0);
}
},
testSetInterval20() {
this._nextTest = this.testSetImmediate;
this._interval = this.setInterval(this._incrementInterval, 20);
}
},
testSetImmediate() {
this.setImmediate(this.testClearTimeout0);
}
},
testClearTimeout0() {
const timeout = this.setTimeout(() => this._fail('testClearTimeout0'), 0);
var timeout = this.setTimeout(() => this._fail('testClearTimeout0'), 0);
this.clearTimeout(timeout);
this.testClearTimeout30();
}
},
testClearTimeout30() {
const timeout = this.setTimeout(() => this._fail('testClearTimeout30'), 30);
var timeout = this.setTimeout(() => this._fail('testClearTimeout30'), 30);
this.clearTimeout(timeout);
this.setTimeout(this.testClearMulti, 50);
}
},
testClearMulti() {
const fails = [];
var fails = [];
fails.push(this.setTimeout(() => this._fail('testClearMulti-1'), 20));
fails.push(this.setTimeout(() => this._fail('testClearMulti-2'), 50));
const delayClear = this.setTimeout(
() => this._fail('testClearMulti-3'),
50,
);
var delayClear = this.setTimeout(() => this._fail('testClearMulti-3'), 50);
fails.push(this.setTimeout(() => this._fail('testClearMulti-4'), 0));
fails.push(this.setTimeout(() => this._fail('testClearMulti-5'), 10));
fails.forEach(timeout => this.clearTimeout(timeout));
fails.forEach((timeout) => this.clearTimeout(timeout));
this.setTimeout(() => this.clearTimeout(delayClear), 20);
this.setTimeout(this.testOrdering, 50);
}
},
testOrdering() {
// Clear timers are set first because it's more likely to uncover bugs.
let fail0;
var fail0;
this.setImmediate(() => this.clearTimeout(fail0));
fail0 = this.setTimeout(
() =>
this._fail(
'testOrdering-t0, setImmediate should happen before ' +
'setTimeout 0',
),
0,
() => this._fail('testOrdering-t0, setImmediate should happen before ' +
'setTimeout 0'),
0
);
let failAnim; // This should fail without the t=0 fastpath feature.
var failAnim; // This should fail without the t=0 fastpath feature.
this.setTimeout(() => this.cancelAnimationFrame(failAnim), 0);
failAnim = this.requestAnimationFrame(() =>
this._fail(
'testOrdering-Anim, setTimeout 0 should happen before ' +
'requestAnimationFrame',
),
failAnim = this.requestAnimationFrame(
() => this._fail('testOrdering-Anim, setTimeout 0 should happen before ' +
'requestAnimationFrame')
);
let fail25;
this.setTimeout(() => {
this.clearTimeout(fail25);
}, 20);
var fail25;
this.setTimeout(() => { this.clearTimeout(fail25); }, 20);
fail25 = this.setTimeout(
() =>
this._fail(
'testOrdering-t25, setTimeout 20 should happen before ' +
'setTimeout 25',
),
25,
() => this._fail('testOrdering-t25, setTimeout 20 should happen before ' +
'setTimeout 25'),
25
);
this.setTimeout(this.done, 50);
}
},
done() {
this.setState({done: true}, () => {
TestModule.markTestCompleted();
});
}
},
componentWillUnmount() {
for (const timeoutID of this._timeoutIDs) {
clearTimeout(timeoutID);
}
for (const intervalID of this._intervalIDs) {
clearInterval(intervalID);
}
for (const requestAnimationFrameID of this._animationFrameIDs) {
cancelAnimationFrame(requestAnimationFrameID);
}
for (const immediateID of this._immediateIDs) {
clearImmediate(immediateID);
}
this._timeoutIDs = new Set();
this._intervalIDs = new Set();
this._animationFrameIDs = new Set();
this._immediateIDs = new Set();
if (this._interval != null) {
clearInterval(this._interval);
this._interval = null;
}
}
render(): React.Node {
render() {
return (
<View style={styles.container}>
<Text>
{this.constructor.name + ': \n'}
{this.constructor.displayName + ': \n'}
Intervals: {this.state.count + '\n'}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
},
_incrementInterval() {
if (this.state.count > 3) {
throw new Error('interval incremented past end.');
}
if (this.state.count === 3) {
if (this._interval != null) {
this.clearInterval(this._interval);
this._interval = null;
}
this.clearInterval(this._interval);
this.setState({count: 0}, this._nextTest);
return;
}
this.setState({count: this.state.count + 1});
}
},
_fail(caller: string): void {
_fail(caller : string) : void {
throw new Error('_fail called by ' + caller);
}
}
},
});
const styles = StyleSheet.create({
var styles = StyleSheet.create({
container: {
backgroundColor: 'white',
padding: 40,
},
});
TimersTest.displayName = 'TimersTest';
module.exports = TimersTest;
+58 -43
View File
@@ -1,37 +1,48 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
* @flow
* @providesModule WebSocketTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
var React = require('react');
var ReactNative = require('react-native');
var { View } = ReactNative;
var { TestModule } = ReactNative.NativeModules;
const DEFAULT_WS_URL = 'ws://localhost:5555/';
const WS_EVENTS = ['close', 'error', 'message', 'open'];
const WS_EVENTS = [
'close',
'error',
'message',
'open',
];
const WS_STATES = [
/* 0 */ 'CONNECTING',
/* 1 */ 'OPEN',
/* 2 */ 'CLOSING',
/* 3 */ 'CLOSED',
];
type State = {
url: string,
fetchStatus: ?string,
socket: ?WebSocket,
socketState: ?number,
lastSocketEvent: ?string,
lastMessage: ?string | ?ArrayBuffer,
testMessage: string,
testExpectedResponse: string,
...
url: string;
fetchStatus: ?string;
socket: ?WebSocket;
socketState: ?number;
lastSocketEvent: ?string;
lastMessage: ?string | ?ArrayBuffer;
testMessage: string;
testExpectedResponse: string;
};
class WebSocketTest extends React.Component<{...}, State> {
class WebSocketTest extends React.Component<{}, State> {
state: State = {
url: DEFAULT_WS_URL,
fetchStatus: null,
@@ -40,12 +51,13 @@ class WebSocketTest extends React.Component<{...}, State> {
lastSocketEvent: null,
lastMessage: null,
testMessage: 'testMessage',
testExpectedResponse: 'testMessage_response',
testExpectedResponse: 'testMessage_response'
};
_waitFor = (condition: any, timeout: any, callback: any) => {
let remaining = timeout;
const timeoutFunction = function() {
var remaining = timeout;
var t;
var timeoutFunction = function() {
if (condition()) {
callback(true);
return;
@@ -54,11 +66,11 @@ class WebSocketTest extends React.Component<{...}, State> {
if (remaining === 0) {
callback(false);
} else {
setTimeout(timeoutFunction, 1000);
t = setTimeout(timeoutFunction,1000);
}
};
setTimeout(timeoutFunction, 1000);
};
t = setTimeout(timeoutFunction,1000);
}
_connect = () => {
const socket = new WebSocket(this.state.url);
@@ -71,11 +83,11 @@ class WebSocketTest extends React.Component<{...}, State> {
_socketIsConnected = () => {
return this.state.socketState === 1; //'OPEN'
};
}
_socketIsDisconnected = () => {
return this.state.socketState === 3; //'CLOSED'
};
}
_disconnect = () => {
if (!this.state.socket) {
@@ -107,41 +119,44 @@ class WebSocketTest extends React.Component<{...}, State> {
};
_receivedTestExpectedResponse = () => {
return this.state.lastMessage === this.state.testExpectedResponse;
return (this.state.lastMessage === this.state.testExpectedResponse);
};
componentDidMount() {
this.testConnect();
}
testConnect: () => void = () => {
this._connect();
this._waitFor(this._socketIsConnected, 5, connectSucceeded => {
testConnect = () => {
var component = this;
component._connect();
component._waitFor(component._socketIsConnected, 5, function(connectSucceeded) {
if (!connectSucceeded) {
TestModule.markTestPassed(false);
return;
}
this.testSendAndReceive();
component.testSendAndReceive();
});
};
}
testSendAndReceive: () => void = () => {
this._sendTestMessage();
this._waitFor(this._receivedTestExpectedResponse, 5, messageReceived => {
testSendAndReceive = () => {
var component = this;
component._sendTestMessage();
component._waitFor(component._receivedTestExpectedResponse, 5, function(messageReceived) {
if (!messageReceived) {
TestModule.markTestPassed(false);
return;
}
this.testDisconnect();
component.testDisconnect();
});
};
}
testDisconnect: () => void = () => {
this._disconnect();
this._waitFor(this._socketIsDisconnected, 5, disconnectSucceeded => {
testDisconnect = () => {
var component = this;
component._disconnect();
component._waitFor(component._socketIsDisconnected, 5, function(disconnectSucceeded) {
TestModule.markTestPassed(disconnectSucceeded);
});
};
}
render(): React.Node {
return <View />;
+61
View File
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule WebViewTest
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
WebView,
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
class WebViewTest extends React.Component {
render() {
var firstMessageReceived = false;
var secondMessageReceived = false;
function processMessage(e) {
var message = e.nativeEvent.data;
if (message === 'First') {firstMessageReceived = true;}
if (message === 'Second') {secondMessageReceived = true;}
// got both messages
if (firstMessageReceived && secondMessageReceived) {TestModule.markTestPassed(true);}
// wait for next message
else if (firstMessageReceived && !secondMessageReceived) {return;}
// first message got lost
else if (!firstMessageReceived && secondMessageReceived) {throw new Error('First message got lost');}
}
var html = 'Hello world'
+ '<script>'
+ "window.setTimeout(function(){window.postMessage('First'); window.postMessage('Second')}, 0)"
+ '</script>';
// fail if messages didn't get through;
window.setTimeout(function() { throw new Error(firstMessageReceived ? 'Both messages got lost' : 'Second message got lost');}, 10000);
var source = {
html: html,
};
return (
<WebView
source={source}
onMessage = {processMessage}
/>
);
}
}
WebViewTest.displayName = 'WebViewTest';
module.exports = WebViewTest;
@@ -1,15 +1,17 @@
#!/bin/bash
#!/usr/bin/env bash
# Copyright (c) Facebook, Inc. and its affiliates.
# Copyright (c) 2015-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
# Set terminal title
echo -en "\033]0;Web Socket Test Server\a"
clear
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
THIS_DIR=$(dirname "$0")
pushd "$THIS_DIR"
./websocket_integration_test_server.js
popd
@@ -1,14 +1,16 @@
#!/usr/bin/env node
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
* @format
* @providesModule websocket_integration_test_server
*/
'use strict';
/* eslint-env node */
@@ -24,13 +26,14 @@ An incoming message of 'exit' will shut down the server.
`);
const server = new WebSocket.Server({port: 5555});
server.on('connection', ws => {
ws.on('message', message => {
server.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('Received message:', message);
if (message === 'exit') {
console.log('WebSocket integration test server exit');
process.exit(0);
}
console.log('Cookie:', ws.upgradeReq.headers.cookie);
ws.send(message + '_response');
});
Vendored
+206
View File
@@ -0,0 +1,206 @@
import groovy.json.JsonSlurperClassic
def runPipeline() {
try {
ansiColor('xterm') {
runStages();
}
} catch(err) {
echo "Error: ${err}"
currentBuild.result = "FAILED"
}
}
def pullDockerImage(imageName) {
def result = sh(script: "docker pull ${imageName}", returnStatus: true)
if (result != 0) {
throw new Exception("Failed to pull image[${imageName}]")
}
}
def buildDockerfile(dockerfilePath = "Dockerfile", imageName) {
def buildCmd = "docker build -f ${dockerfilePath} -t ${imageName} ."
echo "${buildCmd}"
def result = sh(script: buildCmd, returnStatus: true)
if (result != 0) {
throw new Exception("Failed to build image[${imageName}] from '${dockerfilePath}'")
}
}
def runCmdOnDockerImage(imageName, cmd, run_opts = '') {
def result = sh(script: "docker run ${run_opts} -i ${imageName} sh -c '${cmd}'", returnStatus: true)
if(result != 0) {
throw new Exception("Failed to run cmd[${cmd}] on image[${imageName}]")
}
}
def calculateGithubInfo() {
return [
branch: env.BRANCH_NAME,
sha: sh(returnStdout: true, script: 'git rev-parse HEAD').trim(),
tag: null,
isPR: "${env.CHANGE_URL}".contains('/pull/')
]
}
def getParallelInstrumentationTests(testDir, parallelCount, imageName) {
def integrationTests = [:]
def testCount = sh(script: "ls ${testDir} | wc -l", returnStdout: true).trim().toInteger()
def testPerParallel = testCount.intdiv(parallelCount) + 1
def ignoredTests = 'CatalystNativeJavaToJSReturnValuesTestCase|CatalystUIManagerTestCase|CatalystMeasureLayoutTest|CatalystNativeJavaToJSArgumentsTestCase|CatalystNativeJSToJavaParametersTestCase|ReactScrollViewTestCase|ReactHorizontalScrollViewTestCase|ViewRenderingTestCase';
for (def x = 0; (x*testPerParallel) < testCount; x++) {
def offset = x
integrationTests["android integration tests: ${offset}"] = {
run: {
runCmdOnDockerImage(imageName, "bash /app/ContainerShip/scripts/run-android-docker-instrumentation-tests.sh --offset=${offset} --count=${testPerParallel} --ignore=\"${ignoredTests}\"", '--privileged --rm')
}
}
}
return integrationTests
}
def runStages() {
def buildInfo = [
image: [
name: "facebook/react-native",
tag: null
],
scm: [
branch: null,
sha: null,
tag: null,
isPR: false
]
]
node {
def jsDockerBuild, androidDockerBuild
def jsTag, androidTag, jsImageName, androidImageName, parallelInstrumentationTests
try {
stage('Setup') {
parallel(
'pull images': {
pullDockerImage('containership/android-base:latest')
}
)
}
stage('Build') {
checkout scm
def githubInfo = calculateGithubInfo()
buildInfo.scm.branch = githubInfo.branch
buildInfo.scm.sha = githubInfo.sha
buildInfo.scm.tag = githubInfo.tag
buildInfo.scm.isPR = githubInfo.isPR
buildInfo.image.tag = "${buildInfo.scm.sha}-${env.BUILD_TAG.replace(" ", "-").replace("/", "-").replace("%2F", "-")}"
jsTag = "${buildInfo.image.tag}"
androidTag = "${buildInfo.image.tag}"
jsImageName = "${buildInfo.image.name}-js:${jsTag}"
androidImageName = "${buildInfo.image.name}-android:${androidTag}"
parallelInstrumentationTests = getParallelInstrumentationTests('./ReactAndroid/src/androidTest/java/com/facebook/react/tests', 3, androidImageName)
parallel(
'javascript build': {
jsDockerBuild = docker.build("${jsImageName}", "-f ContainerShip/Dockerfile.javascript .")
},
'android build': {
androidDockerBuild = docker.build("${androidImageName}", "-f ContainerShip/Dockerfile.android .")
}
)
}
stage('Tests JS') {
try {
parallel(
'javascript flow': {
runCmdOnDockerImage(jsImageName, 'yarn run flow -- check', '--rm')
},
'javascript tests': {
runCmdOnDockerImage(jsImageName, 'yarn test --maxWorkers=4', '--rm')
},
'documentation tests': {
runCmdOnDockerImage(jsImageName, 'cd website && yarn test', '--rm')
},
'documentation generation': {
runCmdOnDockerImage(jsImageName, 'cd website && node ./server/generate.js', '--rm')
}
)
} catch(e) {
currentBuild.result = "FAILED"
echo "Test JS Stage Error: ${e}"
}
}
stage('Tests Android') {
try {
parallel(
'android unit tests': {
runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-android-docker-unit-tests.sh', '--privileged --rm')
},
'android e2e tests': {
runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-ci-e2e-tests.sh --android --js', '--privileged --rm')
}
)
} catch(e) {
currentBuild.result = "FAILED"
echo "Tests Android Stage Error: ${e}"
}
}
stage('Tests Android Instrumentation') {
// run all tests in parallel
try {
parallel(parallelInstrumentationTests)
} catch(e) {
currentBuild.result = "FAILED"
echo "Tests Android Instrumentation Stage Error: ${e}"
}
}
stage('Cleanup') {
cleanupImage(jsDockerBuild)
cleanupImage(androidDockerBuild)
}
} catch(err) {
cleanupImage(jsDockerBuild)
cleanupImage(androidDockerBuild)
throw err
}
}
}
def isMasterBranch() {
return env.GIT_BRANCH == 'master'
}
def gitCommit() {
return sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
}
def cleanupImage(image) {
if (image) {
try {
sh "docker ps -a | awk '{ print \$1,\$2 }' | grep ${image.id} | awk '{print \$1 }' | xargs -I {} docker rm {}"
sh "docker rmi -f ${image.id}"
} catch(e) {
echo "Error cleaning up ${image.id}"
echo "${e}"
}
}
}
runPipeline()
+26 -17
View File
@@ -1,21 +1,30 @@
MIT License
BSD License
Copyright (c) Facebook, Inc. and its affiliates.
For React Native software
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+7
View File
@@ -0,0 +1,7 @@
{
"rules": {
// This folder currently runs through babel and doesn't need to be
// compatible with node 4
"comma-dangle": 0
}
}
-1
View File
@@ -1 +0,0 @@
__tests__
+491
View File
@@ -0,0 +1,491 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0CF68B051AF0549300FF9E5C /* ARTGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */; };
0CF68B061AF0549300FF9E5C /* ARTNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* ARTNode.m */; };
0CF68B071AF0549300FF9E5C /* ARTRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */; };
0CF68B081AF0549300FF9E5C /* ARTShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE41AF0549300FF9E5C /* ARTShape.m */; };
0CF68B091AF0549300FF9E5C /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; };
0CF68B0A1AF0549300FF9E5C /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; };
0CF68B0B1AF0549300FF9E5C /* ARTBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */; };
0CF68B0C1AF0549300FF9E5C /* ARTLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */; };
0CF68B0D1AF0549300FF9E5C /* ARTPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */; };
0CF68B0E1AF0549300FF9E5C /* ARTRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */; };
0CF68B0F1AF0549300FF9E5C /* ARTSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */; };
0CF68B101AF0549300FF9E5C /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; };
0CF68B111AF0549300FF9E5C /* ARTGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */; };
0CF68B121AF0549300FF9E5C /* ARTNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */; };
0CF68B131AF0549300FF9E5C /* ARTRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */; };
0CF68B141AF0549300FF9E5C /* ARTShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */; };
0CF68B151AF0549300FF9E5C /* ARTSurfaceViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */; };
0CF68B161AF0549300FF9E5C /* ARTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */; };
325CF7AD1E5F2ABA00AC9606 /* ARTBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */; };
325CF7AE1E5F2ABA00AC9606 /* ARTLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */; };
325CF7AF1E5F2ABA00AC9606 /* ARTPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */; };
325CF7B01E5F2ABA00AC9606 /* ARTRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */; };
325CF7B11E5F2ABA00AC9606 /* ARTSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */; };
325CF7B21E5F2ABA00AC9606 /* ARTGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */; };
325CF7B31E5F2ABA00AC9606 /* ARTNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */; };
325CF7B41E5F2ABA00AC9606 /* ARTRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */; };
325CF7B51E5F2ABA00AC9606 /* ARTShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */; };
325CF7B61E5F2ABA00AC9606 /* ARTSurfaceViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */; };
325CF7B71E5F2ABA00AC9606 /* ARTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */; };
325CF7B81E5F2ABA00AC9606 /* ARTGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */; };
325CF7B91E5F2ABA00AC9606 /* ARTNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* ARTNode.m */; };
325CF7BA1E5F2ABA00AC9606 /* ARTRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */; };
325CF7BB1E5F2ABA00AC9606 /* ARTShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE41AF0549300FF9E5C /* ARTShape.m */; };
325CF7BC1E5F2ABA00AC9606 /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; };
325CF7BD1E5F2ABA00AC9606 /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; };
325CF7BE1E5F2ABA00AC9606 /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
0CF68ABF1AF0540F00FF9E5C /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
323A12851E5F266B004975B8 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0CF68AC11AF0540F00FF9E5C /* libART.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libART.a; sourceTree = BUILT_PRODUCTS_DIR; };
0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTCGFloatArray.h; sourceTree = "<group>"; };
0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTContainer.h; sourceTree = "<group>"; };
0CF68ADD1AF0549300FF9E5C /* ARTGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTGroup.h; sourceTree = "<group>"; };
0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTGroup.m; sourceTree = "<group>"; };
0CF68ADF1AF0549300FF9E5C /* ARTNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTNode.h; sourceTree = "<group>"; };
0CF68AE01AF0549300FF9E5C /* ARTNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTNode.m; sourceTree = "<group>"; };
0CF68AE11AF0549300FF9E5C /* ARTRenderable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRenderable.h; sourceTree = "<group>"; };
0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRenderable.m; sourceTree = "<group>"; };
0CF68AE31AF0549300FF9E5C /* ARTShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTShape.h; sourceTree = "<group>"; };
0CF68AE41AF0549300FF9E5C /* ARTShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTShape.m; sourceTree = "<group>"; };
0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSurfaceView.h; sourceTree = "<group>"; };
0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSurfaceView.m; sourceTree = "<group>"; };
0CF68AE71AF0549300FF9E5C /* ARTText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTText.h; sourceTree = "<group>"; };
0CF68AE81AF0549300FF9E5C /* ARTText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTText.m; sourceTree = "<group>"; };
0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextFrame.h; sourceTree = "<group>"; };
0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTBrush.h; sourceTree = "<group>"; };
0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTBrush.m; sourceTree = "<group>"; };
0CF68AED1AF0549300FF9E5C /* ARTLinearGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTLinearGradient.h; sourceTree = "<group>"; };
0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTLinearGradient.m; sourceTree = "<group>"; };
0CF68AEF1AF0549300FF9E5C /* ARTPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPattern.h; sourceTree = "<group>"; };
0CF68AF01AF0549300FF9E5C /* ARTPattern.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPattern.m; sourceTree = "<group>"; };
0CF68AF11AF0549300FF9E5C /* ARTRadialGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRadialGradient.h; sourceTree = "<group>"; };
0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRadialGradient.m; sourceTree = "<group>"; };
0CF68AF31AF0549300FF9E5C /* ARTSolidColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSolidColor.h; sourceTree = "<group>"; };
0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSolidColor.m; sourceTree = "<group>"; };
0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+ART.h"; sourceTree = "<group>"; };
0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+ART.m"; sourceTree = "<group>"; };
0CF68AF91AF0549300FF9E5C /* ARTGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTGroupManager.h; sourceTree = "<group>"; };
0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTGroupManager.m; sourceTree = "<group>"; };
0CF68AFB1AF0549300FF9E5C /* ARTNodeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTNodeManager.h; sourceTree = "<group>"; };
0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTNodeManager.m; sourceTree = "<group>"; };
0CF68AFD1AF0549300FF9E5C /* ARTRenderableManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRenderableManager.h; sourceTree = "<group>"; };
0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRenderableManager.m; sourceTree = "<group>"; };
0CF68AFF1AF0549300FF9E5C /* ARTShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTShapeManager.h; sourceTree = "<group>"; };
0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTShapeManager.m; sourceTree = "<group>"; };
0CF68B011AF0549300FF9E5C /* ARTSurfaceViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTSurfaceViewManager.h; sourceTree = "<group>"; };
0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTSurfaceViewManager.m; sourceTree = "<group>"; };
0CF68B031AF0549300FF9E5C /* ARTTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextManager.h; sourceTree = "<group>"; };
0CF68B041AF0549300FF9E5C /* ARTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTTextManager.m; sourceTree = "<group>"; };
323A12871E5F266B004975B8 /* libART-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libART-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0CF68ABE1AF0540F00FF9E5C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
323A12841E5F266B004975B8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0CF68AB81AF0540F00FF9E5C = {
isa = PBXGroup;
children = (
0CF68AEA1AF0549300FF9E5C /* Brushes */,
0CF68AF81AF0549300FF9E5C /* ViewManagers */,
0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */,
0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */,
0CF68ADD1AF0549300FF9E5C /* ARTGroup.h */,
0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */,
0CF68ADF1AF0549300FF9E5C /* ARTNode.h */,
0CF68AE01AF0549300FF9E5C /* ARTNode.m */,
0CF68AE11AF0549300FF9E5C /* ARTRenderable.h */,
0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */,
0CF68AE31AF0549300FF9E5C /* ARTShape.h */,
0CF68AE41AF0549300FF9E5C /* ARTShape.m */,
0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */,
0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */,
0CF68AE71AF0549300FF9E5C /* ARTText.h */,
0CF68AE81AF0549300FF9E5C /* ARTText.m */,
0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */,
0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */,
0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */,
0CF68AC21AF0540F00FF9E5C /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
0CF68AC21AF0540F00FF9E5C /* Products */ = {
isa = PBXGroup;
children = (
0CF68AC11AF0540F00FF9E5C /* libART.a */,
323A12871E5F266B004975B8 /* libART-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
0CF68AEA1AF0549300FF9E5C /* Brushes */ = {
isa = PBXGroup;
children = (
0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */,
0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */,
0CF68AED1AF0549300FF9E5C /* ARTLinearGradient.h */,
0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */,
0CF68AEF1AF0549300FF9E5C /* ARTPattern.h */,
0CF68AF01AF0549300FF9E5C /* ARTPattern.m */,
0CF68AF11AF0549300FF9E5C /* ARTRadialGradient.h */,
0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */,
0CF68AF31AF0549300FF9E5C /* ARTSolidColor.h */,
0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */,
);
path = Brushes;
sourceTree = "<group>";
};
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
isa = PBXGroup;
children = (
0CF68AF91AF0549300FF9E5C /* ARTGroupManager.h */,
0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */,
0CF68AFB1AF0549300FF9E5C /* ARTNodeManager.h */,
0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */,
0CF68AFD1AF0549300FF9E5C /* ARTRenderableManager.h */,
0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */,
0CF68AFF1AF0549300FF9E5C /* ARTShapeManager.h */,
0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */,
0CF68B011AF0549300FF9E5C /* ARTSurfaceViewManager.h */,
0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */,
0CF68B031AF0549300FF9E5C /* ARTTextManager.h */,
0CF68B041AF0549300FF9E5C /* ARTTextManager.m */,
);
path = ViewManagers;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0CF68AC01AF0540F00FF9E5C /* ART */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0CF68AD51AF0540F00FF9E5C /* Build configuration list for PBXNativeTarget "ART" */;
buildPhases = (
0CF68ABD1AF0540F00FF9E5C /* Sources */,
0CF68ABE1AF0540F00FF9E5C /* Frameworks */,
0CF68ABF1AF0540F00FF9E5C /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = ART;
productName = ART;
productReference = 0CF68AC11AF0540F00FF9E5C /* libART.a */;
productType = "com.apple.product-type.library.static";
};
323A12861E5F266B004975B8 /* ART-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 323A128D1E5F266B004975B8 /* Build configuration list for PBXNativeTarget "ART-tvOS" */;
buildPhases = (
323A12831E5F266B004975B8 /* Sources */,
323A12841E5F266B004975B8 /* Frameworks */,
323A12851E5F266B004975B8 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = "ART-tvOS";
productName = "ART-tvOS";
productReference = 323A12871E5F266B004975B8 /* libART-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0CF68AB91AF0540F00FF9E5C /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0620;
TargetAttributes = {
0CF68AC01AF0540F00FF9E5C = {
CreatedOnToolsVersion = 6.2;
};
323A12861E5F266B004975B8 = {
CreatedOnToolsVersion = 6.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 0CF68ABC1AF0540F00FF9E5C /* Build configuration list for PBXProject "ART" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 0CF68AB81AF0540F00FF9E5C;
productRefGroup = 0CF68AC21AF0540F00FF9E5C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
0CF68AC01AF0540F00FF9E5C /* ART */,
323A12861E5F266B004975B8 /* ART-tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
0CF68ABD1AF0540F00FF9E5C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0CF68B161AF0549300FF9E5C /* ARTTextManager.m in Sources */,
0CF68B111AF0549300FF9E5C /* ARTGroupManager.m in Sources */,
0CF68B0D1AF0549300FF9E5C /* ARTPattern.m in Sources */,
0CF68B0A1AF0549300FF9E5C /* ARTText.m in Sources */,
0CF68B121AF0549300FF9E5C /* ARTNodeManager.m in Sources */,
0CF68B051AF0549300FF9E5C /* ARTGroup.m in Sources */,
0CF68B131AF0549300FF9E5C /* ARTRenderableManager.m in Sources */,
0CF68B091AF0549300FF9E5C /* ARTSurfaceView.m in Sources */,
0CF68B0E1AF0549300FF9E5C /* ARTRadialGradient.m in Sources */,
0CF68B151AF0549300FF9E5C /* ARTSurfaceViewManager.m in Sources */,
0CF68B081AF0549300FF9E5C /* ARTShape.m in Sources */,
0CF68B071AF0549300FF9E5C /* ARTRenderable.m in Sources */,
0CF68B101AF0549300FF9E5C /* RCTConvert+ART.m in Sources */,
0CF68B061AF0549300FF9E5C /* ARTNode.m in Sources */,
0CF68B0F1AF0549300FF9E5C /* ARTSolidColor.m in Sources */,
0CF68B0C1AF0549300FF9E5C /* ARTLinearGradient.m in Sources */,
0CF68B0B1AF0549300FF9E5C /* ARTBrush.m in Sources */,
0CF68B141AF0549300FF9E5C /* ARTShapeManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
323A12831E5F266B004975B8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
325CF7B71E5F2ABA00AC9606 /* ARTTextManager.m in Sources */,
325CF7B21E5F2ABA00AC9606 /* ARTGroupManager.m in Sources */,
325CF7AF1E5F2ABA00AC9606 /* ARTPattern.m in Sources */,
325CF7BD1E5F2ABA00AC9606 /* ARTText.m in Sources */,
325CF7B31E5F2ABA00AC9606 /* ARTNodeManager.m in Sources */,
325CF7B81E5F2ABA00AC9606 /* ARTGroup.m in Sources */,
325CF7B41E5F2ABA00AC9606 /* ARTRenderableManager.m in Sources */,
325CF7BC1E5F2ABA00AC9606 /* ARTSurfaceView.m in Sources */,
325CF7B01E5F2ABA00AC9606 /* ARTRadialGradient.m in Sources */,
325CF7B61E5F2ABA00AC9606 /* ARTSurfaceViewManager.m in Sources */,
325CF7BB1E5F2ABA00AC9606 /* ARTShape.m in Sources */,
325CF7BA1E5F2ABA00AC9606 /* ARTRenderable.m in Sources */,
325CF7BE1E5F2ABA00AC9606 /* RCTConvert+ART.m in Sources */,
325CF7B91E5F2ABA00AC9606 /* ARTNode.m in Sources */,
325CF7B11E5F2ABA00AC9606 /* ARTSolidColor.m in Sources */,
325CF7AE1E5F2ABA00AC9606 /* ARTLinearGradient.m in Sources */,
325CF7AD1E5F2ABA00AC9606 /* ARTBrush.m in Sources */,
325CF7B51E5F2ABA00AC9606 /* ARTShapeManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
0CF68AD31AF0540F00FF9E5C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
};
name = Debug;
};
0CF68AD41AF0540F00FF9E5C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
0CF68AD61AF0540F00FF9E5C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
0CF68AD71AF0540F00FF9E5C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
323A128E1E5F266B004975B8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Debug;
};
323A128F1E5F266B004975B8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0CF68ABC1AF0540F00FF9E5C /* Build configuration list for PBXProject "ART" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0CF68AD31AF0540F00FF9E5C /* Debug */,
0CF68AD41AF0540F00FF9E5C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0CF68AD51AF0540F00FF9E5C /* Build configuration list for PBXNativeTarget "ART" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0CF68AD61AF0540F00FF9E5C /* Debug */,
0CF68AD71AF0540F00FF9E5C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
323A128D1E5F266B004975B8 /* Build configuration list for PBXNativeTarget "ART-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
323A128E1E5F266B004975B8 /* Debug */,
323A128F1E5F266B004975B8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0CF68AB91AF0540F00FF9E5C /* Project object */;
}
+20
View File
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
// A little helper to make sure we have the right memory allocation ready for use.
// We assume that we will only this in one place so no reference counting is necessary.
// Needs to be freed when dealloced.
// This is fragile since this relies on these values not getting reused. Consider
// wrapping these in an Obj-C class or some ARC hackery to get refcounting.
typedef struct {
size_t count;
CGFloat *array;
} ARTCGFloatArray;
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
@protocol ARTContainer <NSObject>
// This is used as a hook for child to mark it's parent as dirty.
// This bubbles up to the root which gets marked as dirty.
- (void)invalidate;
@end
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import "ARTContainer.h"
#import "ARTNode.h"
@interface ARTGroup : ARTNode <ARTContainer>
@property (nonatomic, assign) CGRect clipping;
@end
+26
View File
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTGroup.h"
@implementation ARTGroup
- (void)renderLayerTo:(CGContextRef)context
{
if (!CGRectIsEmpty(self.clipping)) {
CGContextClipToRect(context, self.clipping);
}
for (ARTNode *node in self.subviews) {
[node renderTo:context];
}
}
@end
+32
View File
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/UIView+React.h>
/**
* ART nodes are implemented as empty UIViews but this is just an implementation detail to fit
* into the existing view management. They should also be shadow views and painted on a background
* thread.
*/
@interface ARTNode : UIView
@property (nonatomic, assign) CGFloat opacity;
- (void)invalidate;
- (void)renderTo:(CGContextRef)context;
/**
* renderTo will take opacity into account and draw renderLayerTo off-screen if there is opacity
* specified, then composite that onto the context. renderLayerTo always draws at opacity=1.
* @abstract
*/
- (void)renderLayerTo:(CGContextRef)context;
@end
+82
View File
@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTNode.h"
#import "ARTContainer.h"
@implementation ARTNode
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
[self insertSubview:subview atIndex:atIndex];
[self invalidate];
}
- (void)removeReactSubview:(UIView *)subview
{
[super removeReactSubview:subview];
[self invalidate];
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are inserted by insertReactSubview:
}
- (void)setOpacity:(CGFloat)opacity
{
[self invalidate];
_opacity = opacity;
}
- (void)setTransform:(CGAffineTransform)transform
{
[self invalidate];
super.transform = transform;
}
- (void)invalidate
{
id<ARTContainer> container = (id<ARTContainer>)self.superview;
[container invalidate];
}
- (void)renderTo:(CGContextRef)context
{
if (self.opacity <= 0) {
// Nothing to paint
return;
}
if (self.opacity >= 1) {
// Just paint at full opacity
CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, 1);
[self renderLayerTo:context];
CGContextRestoreGState(context);
return;
}
// This needs to be painted on a layer before being composited.
CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, self.opacity);
CGContextBeginTransparencyLayer(context, NULL);
[self renderLayerTo:context];
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
}
- (void)renderLayerTo:(CGContextRef)context
{
// abstract
}
@end
+25
View File
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import "ARTBrush.h"
#import "ARTCGFloatArray.h"
#import "ARTNode.h"
@interface ARTRenderable : ARTNode
@property (nonatomic, strong) ARTBrush *fill;
@property (nonatomic, assign) CGColorRef stroke;
@property (nonatomic, assign) CGFloat strokeWidth;
@property (nonatomic, assign) CGLineCap strokeCap;
@property (nonatomic, assign) CGLineJoin strokeJoin;
@property (nonatomic, assign) ARTCGFloatArray strokeDash;
@end
+89
View File
@@ -0,0 +1,89 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTRenderable.h"
@implementation ARTRenderable
- (void)setFill:(ARTBrush *)fill
{
[self invalidate];
_fill = fill;
}
- (void)setStroke:(CGColorRef)stroke
{
if (stroke == _stroke) {
return;
}
[self invalidate];
CGColorRelease(_stroke);
_stroke = CGColorRetain(stroke);
}
- (void)setStrokeWidth:(CGFloat)strokeWidth
{
[self invalidate];
_strokeWidth = strokeWidth;
}
- (void)setStrokeCap:(CGLineCap)strokeCap
{
[self invalidate];
_strokeCap = strokeCap;
}
- (void)setStrokeJoin:(CGLineJoin)strokeJoin
{
[self invalidate];
_strokeJoin = strokeJoin;
}
- (void)setStrokeDash:(ARTCGFloatArray)strokeDash
{
if (strokeDash.array == _strokeDash.array) {
return;
}
if (_strokeDash.array) {
free(_strokeDash.array);
}
[self invalidate];
_strokeDash = strokeDash;
}
- (void)dealloc
{
CGColorRelease(_stroke);
if (_strokeDash.array) {
free(_strokeDash.array);
}
}
- (void)renderTo:(CGContextRef)context
{
if (self.opacity <= 0 || self.opacity >= 1 || (self.fill && self.stroke)) {
// If we have both fill and stroke, we will need to paint this using normal compositing
[super renderTo: context];
return;
}
// This is a terminal with only one painting. Therefore we don't need to paint this
// off-screen. We can just composite it straight onto the buffer.
CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, self.opacity);
[self renderLayerTo:context];
CGContextRestoreGState(context);
}
- (void)renderLayerTo:(CGContextRef)context
{
// abstract
}
@end
+76
View File
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ARTSerializablePath
*/
'use strict';
// TODO: Move this into an ART mode called "serialized" or something
var Class = require('art/core/class.js');
var Path = require('art/core/path.js');
var MOVE_TO = 0;
var CLOSE = 1;
var LINE_TO = 2;
var CURVE_TO = 3;
var ARC = 4;
var SerializablePath = Class(Path, {
initialize: function(path) {
this.reset();
if (path instanceof SerializablePath) {
this.path = path.path.slice(0);
} else if (path) {
if (path.applyToPath) {
path.applyToPath(this);
} else {
this.push(path);
}
}
},
onReset: function() {
this.path = [];
},
onMove: function(sx, sy, x, y) {
this.path.push(MOVE_TO, x, y);
},
onLine: function(sx, sy, x, y) {
this.path.push(LINE_TO, x, y);
},
onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y) {
this.path.push(CURVE_TO, p1x, p1y, p2x, p2y, x, y);
},
_arcToBezier: Path.prototype.onArc,
onArc: function(sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) {
if (rx !== ry || rotation) {
return this._arcToBezier(
sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation
);
}
this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1);
},
onClose: function() {
this.path.push(CLOSE);
},
toJSON: function() {
return this.path;
}
});
module.exports = SerializablePath;
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import "ARTRenderable.h"
@interface ARTShape : ARTRenderable
@property (nonatomic, assign) CGPathRef d;
@end
+68
View File
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTShape.h"
@implementation ARTShape
- (void)setD:(CGPathRef)d
{
if (d == _d) {
return;
}
[self invalidate];
CGPathRelease(_d);
_d = CGPathRetain(d);
}
- (void)dealloc
{
CGPathRelease(_d);
}
- (void)renderLayerTo:(CGContextRef)context
{
if ((!self.fill && !self.stroke) || !self.d) {
return;
}
CGPathDrawingMode mode = kCGPathStroke;
if (self.fill) {
if ([self.fill applyFillColor:context]) {
mode = kCGPathFill;
} else {
CGContextSaveGState(context);
CGContextAddPath(context, self.d);
CGContextClip(context);
[self.fill paint:context];
CGContextRestoreGState(context);
if (!self.stroke) {
return;
}
}
}
if (self.stroke) {
CGContextSetStrokeColorWithColor(context, self.stroke);
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeCap);
CGContextSetLineJoin(context, self.strokeJoin);
ARTCGFloatArray dash = self.strokeDash;
if (dash.count) {
CGContextSetLineDash(context, 0, dash.array, dash.count);
}
if (mode == kCGPathFill) {
mode = kCGPathFillStroke;
}
}
CGContextAddPath(context, self.d);
CGContextDrawPath(context, mode);
}
@end
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
#import "ARTContainer.h"
@interface ARTSurfaceView : UIView <ARTContainer>
@end
+54
View File
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTSurfaceView.h"
#import <React/RCTLog.h>
#import "ARTNode.h"
@implementation ARTSurfaceView
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
[self insertSubview:subview atIndex:atIndex];
[self invalidate];
}
- (void)removeReactSubview:(UIView *)subview
{
[super removeReactSubview:subview];
[self invalidate];
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are inserted by insertReactSubview:
}
- (void)invalidate
{
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
for (ARTNode *node in self.subviews) {
[node renderTo:context];
}
}
- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor
{
self.backgroundColor = inheritedBackgroundColor;
}
@end
+20
View File
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import "ARTRenderable.h"
#import "ARTTextFrame.h"
@interface ARTText : ARTRenderable
@property (nonatomic, assign) CTTextAlignment alignment;
@property (nonatomic, assign) ARTTextFrame textFrame;
@end
+127
View File
@@ -0,0 +1,127 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTText.h"
#import <CoreText/CoreText.h>
@implementation ARTText
- (void)setAlignment:(CTTextAlignment)alignment
{
[self invalidate];
_alignment = alignment;
}
static void ARTFreeTextFrame(ARTTextFrame frame)
{
if (frame.count) {
// We must release each line before freeing up this struct
for (int i = 0; i < frame.count; i++) {
CFRelease(frame.lines[i]);
}
free(frame.lines);
free(frame.widths);
}
}
- (void)setTextFrame:(ARTTextFrame)frame
{
if (frame.lines != _textFrame.lines) {
ARTFreeTextFrame(_textFrame);
}
[self invalidate];
_textFrame = frame;
}
- (void)dealloc
{
ARTFreeTextFrame(_textFrame);
}
- (void)renderLayerTo:(CGContextRef)context
{
ARTTextFrame frame = self.textFrame;
if ((!self.fill && !self.stroke) || !frame.count) {
return;
}
// to-do: draw along a path
CGTextDrawingMode mode = kCGTextStroke;
if (self.fill) {
if ([self.fill applyFillColor:context]) {
mode = kCGTextFill;
} else {
for (int i = 0; i < frame.count; i++) {
CGContextSaveGState(context);
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetTextDrawingMode(context, kCGTextClip);
[self renderLineTo:context atIndex:i];
// Inverse the coordinate space back to the original before filling
CGContextScaleCTM(context, 1.0, -1.0);
[self.fill paint:context];
// Restore the state so that the next line can be clipped separately
CGContextRestoreGState(context);
}
if (!self.stroke) {
return;
}
}
}
if (self.stroke) {
CGContextSetStrokeColorWithColor(context, self.stroke);
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeCap);
CGContextSetLineJoin(context, self.strokeJoin);
ARTCGFloatArray dash = self.strokeDash;
if (dash.count) {
CGContextSetLineDash(context, 0, dash.array, dash.count);
}
if (mode == kCGTextFill) {
mode = kCGTextFillStroke;
}
}
CGContextSetTextDrawingMode(context, mode);
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
CGContextScaleCTM(context, 1.0, -1.0);
for (int i = 0; i < frame.count; i++) {
[self renderLineTo:context atIndex:i];
}
}
- (void)renderLineTo:(CGContextRef)context atIndex:(int)index
{
ARTTextFrame frame = self.textFrame;
CGFloat shift;
switch (self.alignment) {
case kCTTextAlignmentRight:
shift = frame.widths[index];
break;
case kCTTextAlignmentCenter:
shift = (frame.widths[index] / 2);
break;
default:
shift = 0;
break;
}
// We should consider snapping this shift to device pixels to improve rendering quality
// when a line has subpixel width.
CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index);
CTLineRef line = frame.lines[index];
CTLineDraw(line, context);
}
@end
+25
View File
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <CoreText/CoreText.h>
// A little helper to make sure we have a set of lines including width ready for use.
// We assume that we will only this in one place so no reference counting is necessary.
// Needs to be freed when dealloced.
// This is fragile since this relies on these values not getting reused. Consider
// wrapping these in an Obj-C class or some ARC hackery to get refcounting.
typedef struct {
size_t count;
CGFloat baseLine; // Distance from the origin to the base line of the first line
CGFloat lineHeight; // Distance between lines
CTLineRef *lines;
CGFloat *widths; // Width of each line
} ARTTextFrame;
+35
View File
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
@interface ARTBrush : NSObject
/* @abstract */
- (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER;
/**
* For certain brushes we can fast path a combined fill and stroke.
* For those brushes we override applyFillColor which sets the fill
* color to be used by those batch paints. Those return YES.
* We can't batch gradient painting in CoreGraphics, so those will
* return NO and paint gets called instead.
* @abstract
*/
- (BOOL)applyFillColor:(CGContextRef)context;
/**
* paint fills the context with a brush. The context is assumed to
* be clipped.
* @abstract
*/
- (void)paint:(CGContextRef)context;
@end
+33
View File
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTBrush.h"
#import <React/RCTDefines.h>
@implementation ARTBrush
- (instancetype)initWithArray:(NSArray *)data
{
return [super init];
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (BOOL)applyFillColor:(CGContextRef)context
{
return NO;
}
- (void)paint:(CGContextRef)context
{
// abstract
}
@end
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTBrush.h"
@interface ARTLinearGradient : ARTBrush
@end
+50
View File
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTLinearGradient.h"
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTLinearGradient
{
CGGradientRef _gradient;
CGPoint _startPoint;
CGPoint _endPoint;
}
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
{
if ((self = [super initWithArray:array])) {
if (array.count < 5) {
RCTLogError(@"-[%@ %@] expects 5 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
_startPoint = [RCTConvert CGPoint:array offset:1];
_endPoint = [RCTConvert CGPoint:array offset:3];
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]);
}
return self;
}
- (void)dealloc
{
CGGradientRelease(_gradient);
}
- (void)paint:(CGContextRef)context
{
CGGradientDrawingOptions extendOptions =
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawLinearGradient(context, _gradient, _startPoint, _endPoint, extendOptions);
}
@end
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTBrush.h"
@interface ARTPattern : ARTBrush
@end
+51
View File
@@ -0,0 +1,51 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTPattern.h"
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTPattern
{
CGImageRef _image;
CGRect _rect;
}
- (instancetype)initWithArray:(NSArray<id /* imagesource + numbers */> *)array
{
if ((self = [super initWithArray:array])) {
if (array.count < 6) {
RCTLogError(@"-[%@ %@] expects 6 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
_image = CGImageRetain([RCTConvert CGImage:array[1]]);
_rect = [RCTConvert CGRect:array offset:2];
}
return self;
}
- (void)dealloc
{
CGImageRelease(_image);
}
// Note: This could use applyFillColor with a pattern. This could be more efficient but
// to do that, we need to calculate our own user space CTM.
- (void)paint:(CGContextRef)context
{
CGContextDrawTiledImage(context, _rect, _image);
}
@end
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTBrush.h"
@interface ARTRadialGradient : ARTBrush
@end
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTRadialGradient.h"
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTRadialGradient
{
CGGradientRef _gradient;
CGPoint _focusPoint;
CGPoint _centerPoint;
CGFloat _radius;
CGFloat _radiusRatio;
}
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
{
if ((self = [super initWithArray:array])) {
if (array.count < 7) {
RCTLogError(@"-[%@ %@] expects 7 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
_radius = [RCTConvert CGFloat:array[3]];
_radiusRatio = [RCTConvert CGFloat:array[4]] / _radius;
_focusPoint.x = [RCTConvert CGFloat:array[1]];
_focusPoint.y = [RCTConvert CGFloat:array[2]] / _radiusRatio;
_centerPoint.x = [RCTConvert CGFloat:array[5]];
_centerPoint.y = [RCTConvert CGFloat:array[6]] / _radiusRatio;
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:7]);
}
return self;
}
- (void)dealloc
{
CGGradientRelease(_gradient);
}
- (void)paint:(CGContextRef)context
{
CGAffineTransform transform = CGAffineTransformMakeScale(1, _radiusRatio);
CGContextConcatCTM(context, transform);
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(context, _gradient, _focusPoint, 0, _centerPoint, _radius, extendOptions);
}
@end
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTBrush.h"
@interface ARTSolidColor : ARTBrush
@end
+40
View File
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTSolidColor.h"
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTSolidColor
{
CGColorRef _color;
}
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
{
if ((self = [super initWithArray:array])) {
_color = CGColorRetain([RCTConvert CGColor:array offset:1]);
}
return self;
}
- (void)dealloc
{
CGColorRelease(_color);
}
- (BOOL)applyFillColor:(CGContextRef)context
{
CGContextSetFillColorWithColor(context, _color);
return YES;
}
@end
+31
View File
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <QuartzCore/QuartzCore.h>
#import <React/RCTConvert.h>
#import "ARTBrush.h"
#import "ARTCGFloatArray.h"
#import "ARTTextFrame.h"
@interface RCTConvert (ART)
+ (CGPathRef)CGPath:(id)json;
+ (CTTextAlignment)CTTextAlignment:(id)json;
+ (ARTTextFrame)ARTTextFrame:(id)json;
+ (ARTCGFloatArray)ARTCGFloatArray:(id)json;
+ (ARTBrush *)ARTBrush:(id)json;
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset;
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset;
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset;
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset;
@end
+224
View File
@@ -0,0 +1,224 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTConvert+ART.h"
#import <React/RCTFont.h>
#import <React/RCTLog.h>
#import "ARTLinearGradient.h"
#import "ARTPattern.h"
#import "ARTRadialGradient.h"
#import "ARTSolidColor.h"
@implementation RCTConvert (ART)
+ (CGPathRef)CGPath:(id)json
{
NSArray *arr = [self NSNumberArray:json];
NSUInteger count = [arr count];
#define NEXT_VALUE [self double:arr[i++]]
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
@try {
NSUInteger i = 0;
while (i < count) {
NSUInteger type = [arr[i++] unsignedIntegerValue];
switch (type) {
case 0:
CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
break;
case 1:
CGPathCloseSubpath(path);
break;
case 2:
CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
break;
case 3:
CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
break;
case 4:
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
break;
default:
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
CGPathRelease(path);
return NULL;
}
}
}
@catch (NSException *exception) {
RCTLogError(@"Invalid CGPath format: %@", arr);
CGPathRelease(path);
return NULL;
}
return (CGPathRef)CFAutorelease(path);
}
RCT_ENUM_CONVERTER(CTTextAlignment, (@{
@"auto": @(kCTTextAlignmentNatural),
@"left": @(kCTTextAlignmentLeft),
@"center": @(kCTTextAlignmentCenter),
@"right": @(kCTTextAlignmentRight),
@"justify": @(kCTTextAlignmentJustified),
}), kCTTextAlignmentNatural, integerValue)
// This takes a tuple of text lines and a font to generate a CTLine for each text line.
// This prepares everything for rendering a frame of text in ARTText.
+ (ARTTextFrame)ARTTextFrame:(id)json
{
NSDictionary *dict = [self NSDictionary:json];
ARTTextFrame frame;
frame.count = 0;
NSArray *lines = [self NSArray:dict[@"lines"]];
NSUInteger lineCount = [lines count];
if (lineCount == 0) {
return frame;
}
CTFontRef font = (__bridge CTFontRef)[self UIFont:dict[@"font"]];
if (!font) {
return frame;
}
// Create a dictionary for this font
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
(NSString *)kCTFontAttributeName:(__bridge id)font,
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
};
// Set up text frame with font metrics
CGFloat size = CTFontGetSize(font);
frame.count = lineCount;
frame.baseLine = size; // estimate base line
frame.lineHeight = size * 1.1; // Base on ART canvas line height estimate
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
frame.widths = malloc(sizeof(CGFloat) * lineCount);
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
CFStringRef string = (__bridge CFStringRef)text;
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString);
CFRelease(attrString);
frame.lines[i] = line;
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
}];
return frame;
}
+ (ARTCGFloatArray)ARTCGFloatArray:(id)json
{
NSArray *arr = [self NSNumberArray:json];
NSUInteger count = arr.count;
ARTCGFloatArray array;
array.count = count;
array.array = NULL;
if (count) {
// Ideally, these arrays should already use the same memory layout.
// In that case we shouldn't need this new malloc.
array.array = malloc(sizeof(CGFloat) * count);
for (NSUInteger i = 0; i < count; i++) {
array.array[i] = [arr[i] doubleValue];
}
}
return array;
}
+ (ARTBrush *)ARTBrush:(id)json
{
NSArray *arr = [self NSArray:json];
NSUInteger type = [self NSUInteger:arr.firstObject];
switch (type) {
case 0: // solid color
// These are probably expensive allocations since it's often the same value.
// We should memoize colors but look ups may be just as expensive.
return [[ARTSolidColor alloc] initWithArray:arr];
case 1: // linear gradient
return [[ARTLinearGradient alloc] initWithArray:arr];
case 2: // radial gradient
return [[ARTRadialGradient alloc] initWithArray:arr];
case 3: // pattern
return [[ARTPattern alloc] initWithArray:arr];
default:
RCTLogError(@"Unknown brush type: %zd", type);
return nil;
}
}
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 2) {
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 2 + offset, arr);
return CGPointZero;
}
return (CGPoint){
[self CGFloat:arr[offset]],
[self CGFloat:arr[offset + 1]],
};
}
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
return CGRectZero;
}
return (CGRect){
{[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]},
{[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]},
};
}
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
return NULL;
}
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
}
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset) {
RCTLogError(@"Too few elements in array (expected at least %zd): %@", offset, arr);
return NULL;
}
arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
ARTCGFloatArray colorsAndOffsets = [self ARTCGFloatArray:arr];
size_t stops = colorsAndOffsets.count / 5;
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(
rgb,
colorsAndOffsets.array,
colorsAndOffsets.array + stops * 4,
stops
);
CGColorSpaceRelease(rgb);
free(colorsAndOffsets.array);
return (CGGradientRef)CFAutorelease(gradient);
}
@end
+586
View File
@@ -0,0 +1,586 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeART
*/
'use strict';
var Color = require('art/core/color');
var Path = require('ARTSerializablePath');
var Transform = require('art/core/transform');
var React = require('React');
var PropTypes = require('prop-types');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var createReactNativeComponentClass = require('createReactNativeComponentClass');
var merge = require('merge');
var invariant = require('fbjs/lib/invariant');
// Diff Helpers
function arrayDiffer(a, b) {
if (a == null || b == null) {
return true;
}
if (a.length !== b.length) {
return true;
}
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return true;
}
}
return false;
}
function fontAndLinesDiffer(a, b) {
if (a === b) {
return false;
}
if (a.font !== b.font) {
if (a.font === null) {
return true;
}
if (b.font === null) {
return true;
}
if (
a.font.fontFamily !== b.font.fontFamily ||
a.font.fontSize !== b.font.fontSize ||
a.font.fontWeight !== b.font.fontWeight ||
a.font.fontStyle !== b.font.fontStyle
) {
return true;
}
}
return arrayDiffer(a.lines, b.lines);
}
// Native Attributes
var SurfaceViewAttributes = merge(ReactNativeViewAttributes.UIView, {
// This should contain pixel information such as width, height and
// resolution to know what kind of buffer needs to be allocated.
// Currently we rely on UIViews and style to figure that out.
});
var NodeAttributes = {
transform: { diff: arrayDiffer },
opacity: true,
};
var GroupAttributes = merge(NodeAttributes, {
clipping: { diff: arrayDiffer }
});
var RenderableAttributes = merge(NodeAttributes, {
fill: { diff: arrayDiffer },
stroke: { diff: arrayDiffer },
strokeWidth: true,
strokeCap: true,
strokeJoin: true,
strokeDash: { diff: arrayDiffer },
});
var ShapeAttributes = merge(RenderableAttributes, {
d: { diff: arrayDiffer },
});
var TextAttributes = merge(RenderableAttributes, {
alignment: true,
frame: { diff: fontAndLinesDiffer },
path: { diff: arrayDiffer }
});
// Native Components
var NativeSurfaceView = createReactNativeComponentClass({
validAttributes: SurfaceViewAttributes,
uiViewClassName: 'ARTSurfaceView',
});
var NativeGroup = createReactNativeComponentClass({
validAttributes: GroupAttributes,
uiViewClassName: 'ARTGroup',
});
var NativeShape = createReactNativeComponentClass({
validAttributes: ShapeAttributes,
uiViewClassName: 'ARTShape',
});
var NativeText = createReactNativeComponentClass({
validAttributes: TextAttributes,
uiViewClassName: 'ARTText',
});
// Utilities
function childrenAsString(children) {
if (!children) {
return '';
}
if (typeof children === 'string') {
return children;
}
if (children.length) {
return children.join('\n');
}
return '';
}
// Surface - Root node of all ART
class Surface extends React.Component {
static childContextTypes = {
isInSurface: PropTypes.bool,
};
getChildContext() {
return { isInSurface: true };
}
render() {
var props = this.props;
var w = extractNumber(props.width, 0);
var h = extractNumber(props.height, 0);
return (
<NativeSurfaceView style={[props.style, { width: w, height: h }]}>
{this.props.children}
</NativeSurfaceView>
);
}
}
// Node Props
// TODO: The desktop version of ART has title and cursor. We should have
// accessibility support here too even though hovering doesn't work.
function extractNumber(value, defaultValue) {
if (value == null) {
return defaultValue;
}
return +value;
}
var pooledTransform = new Transform();
function extractTransform(props) {
var scaleX = props.scaleX != null ? props.scaleX :
props.scale != null ? props.scale : 1;
var scaleY = props.scaleY != null ? props.scaleY :
props.scale != null ? props.scale : 1;
pooledTransform
.transformTo(1, 0, 0, 1, 0, 0)
.move(props.x || 0, props.y || 0)
.rotate(props.rotation || 0, props.originX, props.originY)
.scale(scaleX, scaleY, props.originX, props.originY);
if (props.transform != null) {
pooledTransform.transform(props.transform);
}
return [
pooledTransform.xx, pooledTransform.yx,
pooledTransform.xy, pooledTransform.yy,
pooledTransform.x, pooledTransform.y,
];
}
function extractOpacity(props) {
// TODO: visible === false should also have no hit detection
if (props.visible === false) {
return 0;
}
if (props.opacity == null) {
return 1;
}
return +props.opacity;
}
// Groups
// Note: ART has a notion of width and height on Group but AFAIK it's a noop in
// ReactART.
class Group extends React.Component {
static contextTypes = {
isInSurface: PropTypes.bool.isRequired,
};
render() {
var props = this.props;
invariant(
this.context.isInSurface,
'ART: <Group /> must be a child of a <Surface />'
);
return (
<NativeGroup
opacity={extractOpacity(props)}
transform={extractTransform(props)}>
{this.props.children}
</NativeGroup>
);
}
}
class ClippingRectangle extends React.Component {
render() {
var props = this.props;
var x = extractNumber(props.x, 0);
var y = extractNumber(props.y, 0);
var w = extractNumber(props.width, 0);
var h = extractNumber(props.height, 0);
var clipping = [x, y, w, h];
// The current clipping API requires x and y to be ignored in the transform
var propsExcludingXAndY = merge(props);
delete propsExcludingXAndY.x;
delete propsExcludingXAndY.y;
return (
<NativeGroup
clipping={clipping}
opacity={extractOpacity(props)}
transform={extractTransform(propsExcludingXAndY)}>
{this.props.children}
</NativeGroup>
);
}
}
// Renderables
var SOLID_COLOR = 0;
var LINEAR_GRADIENT = 1;
var RADIAL_GRADIENT = 2;
var PATTERN = 3;
function insertColorIntoArray(color, targetArray, atIndex) {
var c = new Color(color);
targetArray[atIndex + 0] = c.red / 255;
targetArray[atIndex + 1] = c.green / 255;
targetArray[atIndex + 2] = c.blue / 255;
targetArray[atIndex + 3] = c.alpha;
}
function insertColorsIntoArray(stops, targetArray, atIndex) {
var i = 0;
if ('length' in stops) {
while (i < stops.length) {
insertColorIntoArray(stops[i], targetArray, atIndex + i * 4);
i++;
}
} else {
for (var offset in stops) {
insertColorIntoArray(stops[offset], targetArray, atIndex + i * 4);
i++;
}
}
return atIndex + i * 4;
}
function insertOffsetsIntoArray(stops, targetArray, atIndex, multi, reverse) {
var offsetNumber;
var i = 0;
if ('length' in stops) {
while (i < stops.length) {
offsetNumber = i / (stops.length - 1) * multi;
targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber;
i++;
}
} else {
for (var offsetString in stops) {
offsetNumber = (+offsetString) * multi;
targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber;
i++;
}
}
return atIndex + i;
}
function insertColorStopsIntoArray(stops, targetArray, atIndex) {
var lastIndex = insertColorsIntoArray(stops, targetArray, atIndex);
insertOffsetsIntoArray(stops, targetArray, lastIndex, 1, false);
}
function insertDoubleColorStopsIntoArray(stops, targetArray, atIndex) {
var lastIndex = insertColorsIntoArray(stops, targetArray, atIndex);
lastIndex = insertColorsIntoArray(stops, targetArray, lastIndex);
lastIndex = insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, false);
insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, true);
}
function applyBoundingBoxToBrushData(brushData, props) {
var type = brushData[0];
var width = +props.width;
var height = +props.height;
if (type === LINEAR_GRADIENT) {
brushData[1] *= width;
brushData[2] *= height;
brushData[3] *= width;
brushData[4] *= height;
} else if (type === RADIAL_GRADIENT) {
brushData[1] *= width;
brushData[2] *= height;
brushData[3] *= width;
brushData[4] *= height;
brushData[5] *= width;
brushData[6] *= height;
} else if (type === PATTERN) {
// todo
}
}
function extractBrush(colorOrBrush, props) {
if (colorOrBrush == null) {
return null;
}
if (colorOrBrush._brush) {
if (colorOrBrush._bb) {
// The legacy API for Gradients allow for the bounding box to be used
// as a convenience for specifying gradient positions. This should be
// deprecated. It's not properly implemented in canvas mode. ReactART
// doesn't handle update to the bounding box correctly. That's why we
// mutate this so that if it's reused, we reuse the same resolved box.
applyBoundingBoxToBrushData(colorOrBrush._brush, props);
colorOrBrush._bb = false;
}
return colorOrBrush._brush;
}
var c = new Color(colorOrBrush);
return [SOLID_COLOR, c.red / 255, c.green / 255, c.blue / 255, c.alpha];
}
function extractColor(color) {
if (color == null) {
return null;
}
var c = new Color(color);
return [c.red / 255, c.green / 255, c.blue / 255, c.alpha];
}
function extractStrokeCap(strokeCap) {
switch (strokeCap) {
case 'butt': return 0;
case 'square': return 2;
default: return 1; // round
}
}
function extractStrokeJoin(strokeJoin) {
switch (strokeJoin) {
case 'miter': return 0;
case 'bevel': return 2;
default: return 1; // round
}
}
// Shape
// Note: ART has a notion of width and height on Shape but AFAIK it's a noop in
// ReactART.
class Shape extends React.Component {
render() {
var props = this.props;
var path = props.d || childrenAsString(props.children);
var d = (path instanceof Path ? path : new Path(path)).toJSON();
return (
<NativeShape
fill={extractBrush(props.fill, props)}
opacity={extractOpacity(props)}
stroke={extractColor(props.stroke)}
strokeCap={extractStrokeCap(props.strokeCap)}
strokeDash={props.strokeDash || null}
strokeJoin={extractStrokeJoin(props.strokeJoin)}
strokeWidth={extractNumber(props.strokeWidth, 1)}
transform={extractTransform(props)}
d={d}
/>
);
}
}
// Text
var cachedFontObjectsFromString = {};
var fontFamilyPrefix = /^[\s"']*/;
var fontFamilySuffix = /[\s"']*$/;
function extractSingleFontFamily(fontFamilyString) {
// ART on the web allows for multiple font-families to be specified.
// For compatibility, we extract the first font-family, hoping
// we'll get a match.
return fontFamilyString.split(',')[0]
.replace(fontFamilyPrefix, '')
.replace(fontFamilySuffix, '');
}
function parseFontString(font) {
if (cachedFontObjectsFromString.hasOwnProperty(font)) {
return cachedFontObjectsFromString[font];
}
var regexp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm\%]*(?:\s*\/.*?)?\s+)?\s*\"?([^\"]*)/i;
var match = regexp.exec(font);
if (!match) {
return null;
}
var fontFamily = extractSingleFontFamily(match[3]);
var fontSize = +match[2] || 12;
var isBold = /bold/.exec(match[1]);
var isItalic = /italic/.exec(match[1]);
cachedFontObjectsFromString[font] = {
fontFamily: fontFamily,
fontSize: fontSize,
fontWeight: isBold ? 'bold' : 'normal',
fontStyle: isItalic ? 'italic' : 'normal',
};
return cachedFontObjectsFromString[font];
}
function extractFont(font) {
if (font == null) {
return null;
}
if (typeof font === 'string') {
return parseFontString(font);
}
var fontFamily = extractSingleFontFamily(font.fontFamily);
var fontSize = +font.fontSize || 12;
var fontWeight = font.fontWeight != null ? font.fontWeight.toString() : '400';
return {
// Normalize
fontFamily: fontFamily,
fontSize: fontSize,
fontWeight: fontWeight,
fontStyle: font.fontStyle,
};
}
var newLine = /\n/g;
function extractFontAndLines(font, text) {
return { font: extractFont(font), lines: text.split(newLine) };
}
function extractAlignment(alignment) {
switch (alignment) {
case 'right':
return 1;
case 'center':
return 2;
default:
return 0;
}
}
class Text extends React.Component {
render() {
var props = this.props;
var path = props.path;
var textPath = path ? (path instanceof Path ? path : new Path(path)).toJSON() : null;
var textFrame = extractFontAndLines(
props.font,
childrenAsString(props.children)
);
return (
<NativeText
fill={extractBrush(props.fill, props)}
opacity={extractOpacity(props)}
stroke={extractColor(props.stroke)}
strokeCap={extractStrokeCap(props.strokeCap)}
strokeDash={props.strokeDash || null}
strokeJoin={extractStrokeJoin(props.strokeJoin)}
strokeWidth={extractNumber(props.strokeWidth, 1)}
transform={extractTransform(props)}
alignment={extractAlignment(props.alignment)}
frame={textFrame}
path={textPath}
/>
);
}
}
// Declarative fill type objects - API design not finalized
function LinearGradient(stops, x1, y1, x2, y2) {
var type = LINEAR_GRADIENT;
if (arguments.length < 5) {
var angle = ((x1 == null) ? 270 : x1) * Math.PI / 180;
var x = Math.cos(angle);
var y = -Math.sin(angle);
var l = (Math.abs(x) + Math.abs(y)) / 2;
x *= l; y *= l;
x1 = 0.5 - x;
x2 = 0.5 + x;
y1 = 0.5 - y;
y2 = 0.5 + y;
this._bb = true;
} else {
this._bb = false;
}
var brushData = [type, +x1, +y1, +x2, +y2];
insertColorStopsIntoArray(stops, brushData, 5);
this._brush = brushData;
}
function RadialGradient(stops, fx, fy, rx, ry, cx, cy) {
if (ry == null) {
ry = rx;
}
if (cx == null) {
cx = fx;
}
if (cy == null) {
cy = fy;
}
if (fx == null) {
// As a convenience we allow the whole radial gradient to cover the
// bounding box. We should consider dropping this API.
fx = fy = rx = ry = cx = cy = 0.5;
this._bb = true;
} else {
this._bb = false;
}
// The ART API expects the radial gradient to be repeated at the edges.
// To simulate this we render the gradient twice as large and add double
// color stops. Ideally this API would become more restrictive so that this
// extra work isn't needed.
var brushData = [RADIAL_GRADIENT, +fx, +fy, +rx * 2, +ry * 2, +cx, +cy];
insertDoubleColorStopsIntoArray(stops, brushData, 7);
this._brush = brushData;
}
function Pattern(url, width, height, left, top) {
this._brush = [PATTERN, url, +left || 0, +top || 0, +width, +height];
}
var ReactART = {
LinearGradient: LinearGradient,
RadialGradient: RadialGradient,
Pattern: Pattern,
Transform: Transform,
Path: Path,
Surface: Surface,
Group: Group,
ClippingRectangle: ClippingRectangle,
Shape: Shape,
Text: Text,
};
module.exports = ReactART;
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTNodeManager.h"
@interface ARTGroupManager : ARTNodeManager
@end
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ARTGroupManager.h"
#import "ARTGroup.h"
#import "RCTConvert+ART.h"
@implementation ARTGroupManager
RCT_EXPORT_MODULE()
- (ARTNode *)node
{
return [ARTGroup new];
}
RCT_EXPORT_VIEW_PROPERTY(clipping, CGRect)
@end

Some files were not shown because too many files have changed in this diff Show More