Compare commits

..
Author SHA1 Message Date
Hector Ramos a8342a9673 Add missing PerfUtil image.
Summary:
Fixes #14202.
Closes https://github.com/facebook/react-native/pull/14211

Differential Revision: D5141377

Pulled By: hramos

fbshipit-source-id: 9a1b76e94e8b775e84988f1b4bb018e01db8fdb2
2017-07-10 15:25:32 -07:00
Hector Ramos 67e119ded8 [0.45.1] Bump version numbers 2017-06-09 14:47:45 -07:00
Adam Miskiewicz f1cc3ac75a Remove unused default.config.js 2017-06-09 14:47:18 -07:00
Adam Miskiewicz 39e0750223 Fix broken getProjectRoots default in local-cli 2017-06-09 14:47:12 -07:00
Hector Ramos 1668f90f8e Fix sidebar ordering 2017-06-09 14:47:05 -07:00
Hector Ramos c9e24899cb New Handling Touches Tutorial
Summary:
Finally, a place where `Button` is properly introduced. This is based on the old Handling Touches guide, which has been simplified (with some content moved over to the scroll views tutorial).

I've also updated the ordering of the guides into something that makes more sense to someone just getting started with React Native.
Closes https://github.com/facebook/react-native/pull/14371

Differential Revision: D5201127

Pulled By: hramos

fbshipit-source-id: 819192e2db9febb8a315f51693dae557752b6002
2017-06-09 14:46:58 -07:00
Hector Ramos df15af6142 Add Components and APIs Overview Guide
Summary:
Essential components such as View, Text, and Button are easily overlooked amongst the long list of components and APIs due to a lack of categorization. The basic components are already introduced as part of the tutorial, but they may be missed by people who prefer scanning over the docs.

Built website on localhost. Preview of the website as seem on Chrome on macOS:

![screencapture-localhost-8079-react-native-docs-components-and-apis-html-1496357491473](https://cloud.githubusercontent.com/assets/165856/26704237/902d0986-46e2-11e7-9624-17db692ae11e.png)

Preview as seen on an iPhone Plus (via Chrome console):
![screen shot 2017-06-01 at 3 57 00 pm](https://cloud.githubusercontent.com/assets/165856/26704309/05c8ff7e-46e3-11e7-99ff-b7402f5265d4.png)
Closes https://github.com/facebook/react-native/pull/14265

Differential Revision: D5192597

Pulled By: hramos

fbshipit-source-id: f281a14230468458f3da674a82df263fb9f064f4
2017-06-09 14:46:51 -07:00
Mike Grabowski 1a72de8a6d [0.45.0] Bump version numbers 2017-06-07 12:53:53 +02:00
Hector Ramos 3fc67a4391 Update ListView Basics to use FlatList, SectionList.
Summary:
The new list views, FlatList and SectionList, are recommended over ListView.

Built website on localhost and verified the guide is rendered correctly.

![screencapture-localhost-8079-react-native-docs-using-a-listview-html-1495834607096](https://cloud.githubusercontent.com/assets/165856/26513523/c5d2913a-4220-11e7-8c8d-68bb12c75736.png)
Closes https://github.com/facebook/react-native/pull/14210

Differential Revision: D5149151

Pulled By: hramos

fbshipit-source-id: f28f02ee8893c4723c73d610b96ccda51cc31410
2017-06-06 11:20:37 -07:00
Mike Grabowski 446ff94cfe [0.45.0-rc.3] Bump version numbers 2017-06-06 11:14:08 +02:00
Jean Regisser 1ea4002b72 Ship third-party-podspecs in the npm package
Summary:
Hi,

Today I upgraded from RN 0.44 to 0.45.0-rc.0 and noticed I add to include either `CxxBridge` or `BatchedBridge` in the React subspecs in my Podfile to get my project to compile again (https://github.com/facebook/react-native/issues/13010).

Adding `BatchedBridge` works fine. However I wanted to try `CxxBridge` as described in https://github.com/facebook/react-native/commit/5aca739cc25949eebc04a0309c2944f92b5b5391 but couldn't do it since the required `third-party-podspecs` folder with `Folly.podspec`, `GLog.podspec` and `DoubleConversion.podspec` hadn't been included in the npm release.

So here is the fix for that.
It should be included in the next 0.45.0-rc release.

Let me know what you think.
Closes https://github.com/facebook/react-native/pull/13922

Differential Revision: D5051477

Pulled By: javache

fbshipit-source-id: e5c527f1ee9c84734d3e3a3d85ec3f1e5d648bef
2017-06-06 11:07:49 +02:00
Héctor Ramos be67788759 Clarify pros/cons on different navigation libraries
Summary: Closes https://github.com/facebook/react-native/pull/14292

Differential Revision: D5165551

Pulled By: hramos

fbshipit-source-id: a50c1c1eb8d4e7de619b73e1baf621dc11e00abc
2017-06-02 10:22:41 -07:00
Mike Grabowski fa40f5521d [0.45.0-rc.2] Bump version numbers 2017-05-26 15:52:02 +02:00
Mike Grabowski d38f18987d Merge pull request #14208 from rozele/0.45-stable
packager: upgrade jest-haste-map version
2017-05-26 15:51:38 +02:00
Jean Lauliac a35cf5cb32 packager: upgrade jest-haste-map version
Summary: This allows us to get the new fix for recovery on duplicate modules.

Reviewed By: cpojer

Differential Revision: D5128975

fbshipit-source-id: 5a2b60430bbca1806a97798c482af8522366e071
2017-05-26 09:40:30 -04:00
Mike Grabowski 9d479ceb92 [0.45.0-rc.1] Bump version numbers 2017-05-26 09:59:08 +02:00
Mike Grabowski 3ef62a4fe9 Merge branch '0.45-stable' of github.com:facebook/react-native into 0.45-stable 2017-05-26 09:58:54 +02:00
Daniel Zlotin 7c3918b747 XMLHttpRequest withCredentials defaults to "true"
Summary:
see https://github.com/facebook/react-native/issues/14063
Closes https://github.com/facebook/react-native/pull/14064

Differential Revision: D5117654

Pulled By: ericvicenti

fbshipit-source-id: 7c3d376f5251e3b28c34383c5b58658e17d6c032
2017-05-26 09:56:35 +02:00
Hector Ramos 671c46a5e5 Additional doc improvements
Summary:
Mostly cleaning up running on device, integrating with existing apps guides.
Closes https://github.com/facebook/react-native/pull/14165

Differential Revision: D5125590

Pulled By: hramos

fbshipit-source-id: 9239b41a67ab92789e6e409f2715c2cf179fa5f6
2017-05-25 11:00:44 -07:00
Hector Ramos b91e04f61f Expand Getting Started, Debugging, Running on Device guides.
Summary:
- Clarified PATH instructions.
- Expand running on device instructions for iOS.
- Add Android production release instructions.
- Misc. cleanup.
Closes https://github.com/facebook/react-native/pull/14142

Differential Revision: D5116072

Pulled By: hramos

fbshipit-source-id: d95274f61a9b26282977d43d3307b726a6b93704
2017-05-25 11:00:35 -07:00
Hector Ramos ff6189b9be Update Getting Started, Upgrading for CRNA
Summary:
Restored the version lag caveat in the Getting Started, and updated the Upgrading guide to account for CRNA.

Additionally, updated the release versions page to account for the new release cycle (e.g. release candidates are encouraged).
Closes https://github.com/facebook/react-native/pull/14112

Differential Revision: D5112224

Pulled By: hramos

fbshipit-source-id: d544c5178484c27f1ed6dee3479bcd94e5e81982
2017-05-25 11:00:05 -07:00
Hector Ramos 55501c3ba8 Add tabs to Getting Started and update Android Studio steps
Summary:
There are two ways to get started with React Native: using CRNA, or by building native code. The latter requires a set of dependencies that also need to be installed even by people who use CRNA if they have ejected.

There needs to be a clear demarcation between the two approaches as well as clear instructions as to which steps are of interest to CRNA using parties or people adding RN to an existing app.

Separating the two approaches entirely by using tabs can achieve this purpose.

Built and tested the website on Chrome on macOS. Have not yet tested other browsers or OS configurations.

![screencapture-localhost-8079-react-native-docs-getting-started-html-1494974542733](https://cloud.githubusercontent.com/assets/165856/26131374/77258b0c-3a4e-11e7-9a17-074610ca33d1.png)

![screencapture-localhost-8079-react-native-docs-getting-started-html-1494974580882](https://cloud.githubusercontent.com/assets/165856/26131370/73806990-3a4e-11e7-9e67-73c519b050da.png)

This is a work in progress. Feedback is appreciated. I am not yet happy with the different sets of tabs/buttons, for example.
Closes https://github.com/facebook/react-native/pull/14005

Differential Revision: D5096426

Pulled By: hramos

fbshipit-source-id: cdbf19b148ec87f2f2dfdced345084d0bbff4c26
2017-05-25 10:59:47 -07:00
Adam Perry 2f9ece4e63 Update examples in docs and address version lag of CRNA
Summary:
cc hramos

Pretty sure I've hit all of the places where AppRegistry is called in CRNA-pastable examples. Let me know whether you think we need to approach the version lag differently, I figure a caveat is as natural a place to call it out as any.

If you end up finding anything else that needs tweaking before cherry picking, I'm happy to push that up here too.
Closes https://github.com/facebook/react-native/pull/13744

Differential Revision: D5071038

Pulled By: hramos

fbshipit-source-id: 4a4a6f2a73079aca627f17d75a4e4b395ecbd4a8
2017-05-25 10:59:20 -07:00
Héctor Ramos 5c6a168a28 Updated Android Studio steps
Summary:
Clarified some steps unique to the Windows installer.
Closes https://github.com/facebook/react-native/pull/13983

Differential Revision: D5065884

Pulled By: hramos

fbshipit-source-id: ab47ad7e7fcf10f8b44ab209807d661c465b76a3
2017-05-25 10:58:39 -07:00
Mike Grabowski 65683a6940 [0.45.0-rc.0] Bump version numbers 2017-05-11 12:00:55 +02:00
2579 changed files with 126242 additions and 111671 deletions
+1
View File
@@ -9,4 +9,5 @@
central = https://repo1.maven.org/maven2
[alias]
movies = //Examples/Movies/android/app:app
rntester = //RNTester/android/app:app
-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.
-616
View File
@@ -1,616 +0,0 @@
aliases:
# Cache Management
- &restore-yarn-cache
keys:
- v1-yarn-{{ arch }}-{{ checksum "package.json" }}
# Fallback in case checksum fails
- v1-yarn-{{ arch }}-
- &save-yarn-cache
paths:
- node_modules
- ~/.cache/yarn
key: v1-yarn-{{ arch }}-{{ checksum "package.json" }}
- &restore-cache-analysis
keys:
- v1-analysis-dependencies-{{ arch }}-{{ checksum "package.json" }}{{ checksum "bots/package.json" }}
# Fallback in case checksum fails
- v1-analysis-dependencies-{{ arch }}-
- &save-cache-analysis
paths:
- bots/node_modules
- node_modules
key: v1-analysis-dependencies-{{ arch }}-{{ checksum "package.json" }}{{ checksum "bots/package.json" }}
- &restore-cache-android-packages
keys:
- v1-android-sdkmanager-packages-{{ arch }}-api-26-alpha-{{ checksum "scripts/circle-ci-android-setup.sh" }}
# Fallback in case checksum fails
- v1-android-sdkmanager-packages-{{ arch }}-api-26-alpha-
- &save-cache-android-packages
paths:
- /opt/android/sdk
key: v1-android-sdkmanager-packages-{{ arch }}-api-26-alpha-{{ checksum "scripts/circle-ci-android-setup.sh" }}
- &restore-cache-ndk
keys:
- v2-android-ndk-{{ arch }}-r10e-32-64
- &save-cache-ndk
paths:
- /opt/ndk
key: v2-android-ndk-{{ arch }}-r10e-32-64
- &restore-cache-buck
keys:
- v2-buck-{{ arch }}-v2017.11.16.01
- &save-cache-buck
paths:
- ~/buck
key: v2-buck-{{ arch }}-v2017.11.16.01
- &restore-cache-watchman
keys:
- v1-watchman-{{ arch }}-v4.9.0
- &save-cache-watchman
paths:
- ~/watchman
key: v1-watchman-{{ arch }}-v4.9.0
# Branch Filtering
- &filter-only-master-stable
branches:
only:
- /.*-stable/
- master
- &filter-only-stable
branches:
only:
- /.*-stable/
- &filter-ignore-gh-pages
branches:
ignore: gh-pages
- &filter-ignore-master-stable
branches:
ignore:
- master
- /.*-stable/
- gh-pages
# Dependency Management
- &install-ndk
|
source scripts/circle-ci-android-setup.sh && getAndroidNDK
- &yarn
|
yarn install --non-interactive --cache-folder ~/.cache/yarn
- &install-yarn
|
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
- &install-node-dependencies
|
npm install --no-package-lock --no-spin --no-progress
- &install-buck
|
if [[ ! -e ~/buck ]]; then
git clone https://github.com/facebook/buck.git ~/buck --branch v2017.11.16.01 --depth=1
fi
cd ~/buck && ant
buck --version
- &install-node
|
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
- &create-ndk-directory
name: Create Android NDK Directory
command: |
if [[ ! -e /opt/ndk ]]; then
sudo mkdir /opt/ndk
fi
sudo chown ${USER:=$(/usr/bin/id -run)}:$USER /opt/ndk
# CircleCI does not support interpolating env variables in the environment
# https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables
- &configure-android-path
name: Configure Environment Variables
command: |
echo 'export PATH=${ANDROID_NDK}:~/react-native/gradle-2.9/bin:~/buck/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
- &install-android-packages
|
source scripts/circle-ci-android-setup.sh && getAndroidSDK
- &install-android-build-dependencies
name: Install Android Build Dependencies
command: |
sudo apt-get update -y
sudo apt-get install ant autoconf automake g++ gcc libqt5widgets5 lib32z1 lib32stdc++6 make maven python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev file -y
# Test Definitions
- &run-js-tests
name: JavaScript Test Suite
command: yarn test-ci
- &run-lint-checks
name: Lint code
command: yarn lint --format junit -o ~/react-native/reports/junit/js-lint-results.xml
when: always
- &run-flow-checks
name: Check for errors in code using Flow
command: yarn flow check
when: always
- &build-android-app
name: Build Android App
command: |
buck build ReactAndroid/src/main/java/com/facebook/react
buck build ReactAndroid/src/main/java/com/facebook/react/shell
- &create-avd
name: Create Android Virtual Device
command: source scripts/circle-ci-android-setup.sh && createAVD
- &launch-avd
name: Launch Android Virtual Device in Background
command: source scripts/circle-ci-android-setup.sh && launchAVD
background: true
- &wait-for-avd
name: Wait for Android Virtual Device
command: source scripts/circle-ci-android-setup.sh && waitForAVD
- &build-js-bundle
name: Build JavaScript Bundle
command: node local-cli/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
- &compile-native-libs
name: Compile Native Libs for Unit and Integration Tests
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
no_output_timeout: 6m
- &run-android-unit-tests
name: Run Unit Tests
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS
- &run-android-instrumentation-tests
name: Run Instrumentation Tests
command: |
if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
echo "JavaScript bundle missing, cannot run instrumentation tests. Verify build-js-bundle step completed successfully."; exit 1;
fi
source scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS
- &collect-android-test-results
name: Collect Test Results
command: |
find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/react-native/reports/junit/ \;
find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/react-native/reports/junit/ \;
find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ~/react-native/reports/junit/ \;
when: always
- &setup-artifacts
name: Initial Setup
command: |
mkdir -p ~/react-native/reports/junit/
- &run-objc_ios-tests
name: Objective-C iOS Test Suite
command: ./scripts/objc-test-ios.sh
- &run-objc-tvos-tests
name: Objective-C tvOS Test Suite
command: ./scripts/objc-test-tvos.sh
- &run-objc-ios-e2e-tests
name: Objective-C iOS End-to-End Test Suite
command: node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3;
- &run-objc-tvos-e2e-tests
name: Objective-C tvOS End-to-End Test Suite
command: node ./scripts/run-ci-e2e-tests.js --tvos --js --retries 3;
defaults: &defaults
working_directory: ~/react-native
js_defaults: &js_defaults
<<: *defaults
docker:
- image: circleci/node:8
android_defaults: &android_defaults
<<: *defaults
docker:
- image: circleci/android:api-26-alpha
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"'
- ANDROID_NDK: '/opt/ndk/android-ndk-r10e'
- BUILD_THREADS: 2
macos_defaults: &macos_defaults
<<: *defaults
macos:
xcode: "9.0"
version: 2
jobs:
# Set up a Node environment for downstream jobs
checkout_code:
<<: *js_defaults
steps:
- checkout
- run: *setup-artifacts
- restore-cache: *restore-yarn-cache
- run: *yarn
- save-cache: *save-yarn-cache
- persist_to_workspace:
root: .
paths: .
# Runs JavaScript lint and flow checks.
# Currently will fail a PR if lint/flow raises issues.
js_checks:
<<: *js_defaults
steps:
- attach_workspace:
at: ~/react-native
- run: *run-lint-checks
- run: *run-flow-checks
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/yarn.lock
# Runs JavaScript tests on Node 8
test_javascript:
<<: *js_defaults
steps:
- attach_workspace:
at: ~/react-native
- run: *run-js-tests
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
# Runs JavaScript tests on Node 6
test_javascript_node6_compatibility:
<<: *defaults
docker:
- image: circleci/node:6
steps:
- checkout
- run: *setup-artifacts
- restore-cache: *restore-yarn-cache
- run: *yarn
- save-cache: *save-yarn-cache
- run: *run-js-tests
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
# Runs unit tests on iOS devices
test_objc_ios:
<<: *macos_defaults
dependencies:
pre:
- xcrun instruments -w "iPhone 5s (10.3.1)" || true
steps:
- attach_workspace:
at: ~/react-native
- run: *run-objc_ios-tests
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
# Runs unit tests on tvOS devices
test_objc_tvos:
<<: *macos_defaults
dependencies:
pre:
- xcrun instruments -w "Apple TV 1080p (10.0)" || true
steps:
- attach_workspace:
at: ~/react-native
- run: *run-objc-tvos-tests
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
# Runs end to end tests
test_objc_ios_e2e:
<<: *macos_defaults
dependencies:
pre:
- xcrun instruments -w "iPhone 5s (10.3.1)" || true
steps:
- attach_workspace:
at: ~/react-native
- run: *run-objc-ios-e2e-tests
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
# Checks podspec
test_podspec:
<<: *defaults
macos:
xcode: "9.0"
steps:
- attach_workspace:
at: ~/react-native
- run: ./scripts/process-podspecs.sh
# Publishes new version onto npm
publish_npm_package:
<<: *android_defaults
steps:
# Checkout code so that we can work with `git` in publish.js
- checkout
- attach_workspace:
at: ~/react-native
# Configure Android SDK and related dependencies
- run: *configure-android-path
- run: *install-android-build-dependencies
- restore-cache: *restore-cache-android-packages
- run: *install-android-packages
- save-cache: *save-cache-android-packages
# Install Android NDK
- run: *create-ndk-directory
- restore-cache: *restore-cache-ndk
- run: *install-ndk
- save-cache: *save-cache-ndk
# Fetch dependencies using BUCK
- restore-cache: *restore-cache-buck
- run: *install-buck
- save-cache: *save-cache-buck
- 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: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
- run: *install-node
- run: *install-yarn
- restore-cache: *restore-yarn-cache
- run: *yarn
- save-cache: *save-yarn-cache
- run:
name: Publish React Native Package
command: |
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
git config --global user.email "reactjs-bot@users.noreply.github.com"
git config --global user.name "npm Deployment Script"
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
node ./scripts/publish-npm.js
else
echo "Skipping deploy."
fi
# Set up an Android environment for downstream jobs
test_android:
<<: *android_defaults
steps:
- attach_workspace:
at: ~/react-native
# Configure Android SDK and related dependencies
- run: *configure-android-path
- run: *install-android-build-dependencies
- restore-cache: *restore-cache-android-packages
- run: *install-android-packages
- save-cache: *save-cache-android-packages
# Starting emulator in advance as it takes some time to boot.
- run: *create-avd
- run: *launch-avd
# Keep configuring Android dependencies while AVD boots up
# Install Android NDK
- run: *create-ndk-directory
- restore-cache: *restore-cache-ndk
- run: *install-ndk
- save-cache: *save-cache-ndk
# Fetch dependencies using BUCK
- restore-cache: *restore-cache-buck
- run: *install-buck
- save-cache: *save-cache-buck
- 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: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
# Build and compile
- run: *build-android-app
- run: *compile-native-libs
# Build JavaScript Bundle for instrumentation tests
- run: *install-node
- run: *build-js-bundle
# Wait for AVD to finish booting before running tests
- run: *wait-for-avd
# Test Suite
- run: *run-android-unit-tests
- run: *run-android-instrumentation-tests
# post (always runs)
- run: *collect-android-test-results
- store_test_results:
path: ~/react-native/reports/junit
- store_artifacts:
path: ~/react-native/reports/junit
# 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:
<<: *js_defaults
steps:
- attach_workspace:
at: ~/react-native
- restore-cache: *restore-cache-analysis
- run: *yarn
- run:
name: Install Additional Dependencies
command: |
if [ -n "$CIRCLE_PR_NUMBER" ]; then
yarn add github@0.2.4
cd bots
yarn install --non-interactive --cache-folder ~/.cache/yarn
else
echo "Skipping dependency installation."
fi
- save-cache: *save-cache-analysis
- run:
name: Analyze Pull Request
command: |
if [ -n "$CIRCLE_PR_NUMBER" ]; then
cd bots && DANGER_GITHUB_API_TOKEN="e622517d9f1136ea8900""07c6373666312cdfaa69" yarn danger
else
echo "Skipping pull request analysis."
fi
when: always
- run:
name: Analyze Code
command: |
if [ -n "$CIRCLE_PR_NUMBER" ]; then
cat <(echo eslint; yarn --silent lint --format=json; echo flow; yarn --silent flow check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js
else
echo "Skipping code analysis."
fi
when: always
# Workflows enables us to run multiple jobs in parallel
workflows:
version: 2
test:
jobs:
# Checkout repo and run Yarn
- checkout_code:
filters: *filter-ignore-gh-pages
# Run lint and flow checks
- js_checks:
filters: *filter-ignore-gh-pages
requires:
- checkout_code
# Test JavaScript
- test_javascript:
filters: *filter-ignore-gh-pages
requires:
- checkout_code
# Test JavaScript using Node 6, the minimum supported version
- test_javascript_node6_compatibility:
filters: *filter-ignore-gh-pages
# Test Android
- test_android:
filters: *filter-ignore-gh-pages
requires:
- checkout_code
# Test iOS & tvOS
- test_objc_ios:
filters: *filter-ignore-gh-pages
requires:
- checkout_code
- test_objc_tvos:
filters: *filter-ignore-gh-pages
requires:
- checkout_code
# End-to-end tests
- test_objc_ios_e2e:
filters: *filter-ignore-gh-pages
requires:
- checkout_code
# Only runs on PRs
analyze:
jobs:
# Checkout repo and run Yarn
- checkout_code:
filters: *filter-ignore-master-stable
# Run code checks
- analyze_pr:
filters: *filter-ignore-master-stable
requires:
- checkout_code
# Only runs on NN-stable branches
deploy:
jobs:
# Checkout repo and run Yarn
- checkout_code:
filters: *filter-only-stable
# If we are on a stable branch, wait for approval to deploy to npm
- approve_publish_npm_package:
filters: *filter-only-stable
type: approval
- publish_npm_package:
requires:
- checkout_code
- approve_publish_npm_package
+1 -5
View File
@@ -3,8 +3,4 @@
**/staticBundle.js
**/main.js
Libraries/vendor/**/*
Libraries/Renderer/*
pr-inactivity-bookmarklet.js
question-bookmarklet.js
flow/
danger/
packager/src/worker-farm/**/*
+38 -54
View File
@@ -1,19 +1,17 @@
{
"root": true,
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"env": {
"es6": true,
"jest": true,
"jasmine": true,
},
"plugins": [
"eslint-comments",
"flowtype",
"prettier",
"react",
"jest"
"react"
],
// Map from global var to bool specifying if it can be redefined
@@ -51,30 +49,41 @@
"setTimeout": false,
"window": false,
"XMLHttpRequest": false,
"pit": false
"pit": false,
// Flow global types.
"ReactComponent": false,
"ReactClass": false,
"ReactElement": false,
"ReactPropsCheckType": false,
"ReactPropsChainableTypeChecker": false,
"ReactPropTypes": false,
"SyntheticEvent": false,
"$Either": false,
"$All": false,
"$ArrayBufferView": false,
"$Tuple": false,
"$Supertype": false,
"$Subtype": false,
"$Shape": false,
"$Diff": false,
"$Keys": false,
"$Enum": false,
"$Exports": false,
"$FlowIssue": false,
"$FlowFixMe": false,
"$FixMe": false
},
"rules": {
// 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"
}],
"comma-dangle": 0, // disallow trailing commas in object literals
"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": 2, // disallow duplicate keys when creating object literals
"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
@@ -88,7 +97,7 @@
"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": 2, // disallow unreachable statements after a return, throw, continue, or break statement
"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
@@ -109,7 +118,7 @@
"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": 2, // disallow use of eval()
"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
@@ -122,7 +131,7 @@
"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": 2, // disallow use of new operator for Function object
"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";
@@ -167,33 +176,17 @@
"no-restricted-modules": 1, // restrict usage of specified node modules (off by default)
"no-sync": 0, // disallow use of synchronous methods (off by default)
// ESLint Comments Plugin
// The following rules are made available via `eslint-plugin-eslint-comments`
'eslint-comments/no-aggregating-enable': 1, // disallows eslint-enable comments for multiple eslint-disable comments
'eslint-comments/no-unlimited-disable': 1, // disallows eslint-disable comments without rule names
'eslint-comments/no-unused-disable': 1, // disallow disables that don't cover any errors
'eslint-comments/no-unused-enable': 1, // // disallow enables that don't enable anything or enable rules that weren't disabled
// Flow Plugin
// The following rules are made available via `eslint-plugin-flowtype`
"flowtype/define-flow-type": 1,
"flowtype/use-flow-type": 1,
// 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"], // enforces the usage of double quotes for all JSX attribute values which doesnt contain a double quote
"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, // enforces consistent naming when capturing the current execution context (off by default)
"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)
@@ -201,7 +194,6 @@
"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-empty-character-class': 1, // disallow the use of empty character classes in regular expressions
"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
@@ -236,9 +228,8 @@
"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": 2,
"react/jsx-no-undef": 1,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
@@ -251,12 +242,5 @@
"react/react-in-jsx-scope": 1,
"react/self-closing-comp": 1,
"react/wrap-multilines": 0,
// Jest Plugin
// The following rules are made available via `eslint-plugin-jest`.
"jest/no-disabled-tests": 1,
"jest/no-focused-tests": 1,
"jest/no-identical-title": 1,
"jest/valid-expect": 1,
}
}
+11 -14
View File
@@ -5,8 +5,8 @@
; Ignore templates for 'react-native init'
.*/local-cli/templates/.*
; Ignore the Dangerfile
<PROJECT_ROOT>/bots/dangerfile.js
; Ignore the website subdir
<PROJECT_ROOT>/website/.*
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
@@ -18,38 +18,35 @@
; 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/.*
; Ignore metro
.*/node_modules/metro/.*
.*/Libraries/react-native/ReactNative.js
[include]
[libs]
Libraries/react-native/react-native-interface.js
flow/
flow-github/
[options]
emoji=true
module.system=haste
experimental.strict_type_args=true
munge_underscores=true
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=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-5]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-5]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
^0.65.0
^0.45.0
-14
View File
@@ -1,14 +0,0 @@
Libraries/Animated/* @janicduplessis
Libraries/NativeAnimation/* @janicduplessis
Libraries/Image/* @shergin
Libraries/Text/* @shergin
React/Base/* @shergin
React/Views/* @shergin
React/Modules/* @shergin
React/CxxBridge/* @mhorowitz
ReactAndroid/src/main/java/com/facebook/react/animated/* @janicduplessis
**/*.md @hramos
package.json @hramos
local-cli/core/* @grabbou @kureev
local-cli/link/* @grabbou @kureev
local-cli/unlink/* @grabbou @kureev
+22 -108
View File
@@ -1,122 +1,36 @@
<!--
PLEASE DON'T DELETE THIS TEMPLATE UNTIL YOU HAVE READ THE FIRST SECTION.
-->
Please read the following carefully before opening a new issue.
Your issue may be closed if it does not provide the information required by this template.
### Is this a bug report?
We use GitHub Issues exclusively for tracking bugs in React Native.
(write your answer here)
- If you have a question, ask on Stack Overflow: http://stackoverflow.com/questions/tagged/react-native
- If you have a feature request, post it on Canny: https://react-native.canny.io/feature-requests
<!--
If you answered "Yes":
Make sure your issue reproduces on master. Your issue may already have been fixed!
If your issue is present in master and in the stable release, try to reproduce your bug on https://snack.expo.io/
If you can't reproduce the bug on Snack, provide a sample project.
At the very least, provide an example of your code.
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.
--- Delete everything above this line ---
If you answered "No":
### Description
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/help.html list various resources
that should help you get started.
Explain what you did, what you expected to happen, and what actually happens.
Now scroll below!
-->
### Reproduction Steps and Sample Code
### Have you read the [Contributing Guidelines](https://facebook.github.io/react-native/docs/contributing.html)?
List all the steps required to reproduce the issue you're reporting. These steps should be clear and concise.
(Write your answer here.)
***An example of your code is REQUIRED***
### Environment
<!--
In your terminal run `react-native info` and paste its contents here. Next, specify your target platform, like this:
### Solution
Environment:
OS: macOS Sierra 10.12.6
Node: 8.4.0
Yarn: 0.27.5
npm: 5.4.0
Watchman: 4.7.0
Xcode: Xcode 8.3.3 Build version 8E3004b
Android Studio: 2.3 AI-162.4069837
What needs to be done to address this issue? Ideally, provide a pull request with a fix.
Packages: (wanted => installed)
react-native: 0.48.1 => 0.48.1
react: 16.0.0-alpha.12 => 16.0.0-alpha.12
### Additional Information
Target Platform: iOS (10.3)
***Make sure you are on v0.48.0 or greater of react-native, otherwise you may get this error:
Unrecognized command 'info'
Run react-native --help to see list of all available commands
-->
### 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!
-->
* React Native version: [FILL THIS OUT: Be specific, filling out "latest" here is not enough.]
* Platform: [FILL THIS OUT: iOS, Android, or both?]
* Development Operating System: [FILL THIS OUT: Are you developing on MacOS, Linux, or Windows?]
* Dev tools: [FILL THIS OUT: Xcode or Android Studio version, iOS or Android SDK version, if applicable]
+20 -38
View File
@@ -1,50 +1,32 @@
<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.
Thanks for submitting a PR! Please read these instructions carefully:
Help us understand your motivation by explaining why you decided to make this change.
- [ ] Explain the **motivation** for making this change.
- [ ] Provide a **test plan** demonstrating that the code is solid.
- [ ] Match the **code formatting** of the rest of the codebase.
- [ ] Target the `master` branch, NOT a "stable" branch.
You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html
## Motivation (required)
Happy contributing!
What existing problem does the pull request solve?
-->
## Test Plan (required)
## Motivation
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. See [What is a Test Plan?][1] to learn more.
(Write your motivation here.)
If you have added code that should be tested, add tests.
## Test Plan
## Next Steps
(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!)
Sign the [CLA][2], if you haven't already.
## Related PRs
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.
(If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/react-native-website, and link to your PR here.)
Make sure all **tests pass** on both [Travis][3] and [Circle CI][4]. PRs that break tests are unlikely to be merged.
## Release Notes
<!--
Help reviewers and the release process by writing your own release notes
For more info, see the ["Pull Requests"][5] section of our "Contributing" guidelines.
**INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.**
CATEGORY
[----------] TYPE
[ CLI ] [-------------] LOCATION
[ DOCS ] [ BREAKING ] [-------------]
[ GENERAL ] [ BUGFIX ] [-{Component}-]
[ INTERNAL ] [ ENHANCEMENT ] [ {File} ]
[ IOS ] [ FEATURE ] [ {Directory} ] |-----------|
[ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} |
[----------] [-------------] [-------------] |-----------|
[CATEGORY] [TYPE] [LOCATION] - MESSAGE
EXAMPLES:
[IOS] [BREAKING] [FlatList] - Change a thing that breaks other things
[ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput
[CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with
[DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word
[GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position
[INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see
-->
[1]: https://medium.com/@martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9
[2]: https://code.facebook.com/cla
[3]: https://travis-ci.org/facebook/react-native
[4]: http://circleci.com/gh/facebook/react-native
[5]: https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests
-9
View File
@@ -1,9 +0,0 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 14
# Label requiring a response
responseRequiredLabel: Needs more information
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
Closing this issue as more information is needed to debug this and we have not heard back from the author.
-20
View File
@@ -1,20 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# 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
- For Discussion
- Core Team
# 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: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people.
If you think this issue should definitely remain open, please let us know why.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+4 -12
View File
@@ -23,10 +23,10 @@ project.xcworkspace
# Gradle
/build/
/RNTester/android/app/build/
/RNTester/android/app/gradle/
/RNTester/android/app/gradlew
/RNTester/android/app/gradlew.bat
/Examples/**/android/app/build/
/Examples/**/android/app/gradle/
/Examples/**/android/app/gradlew
/Examples/**/android/app/gradlew.bat
/ReactAndroid/build/
# Buck
@@ -36,9 +36,6 @@ buck-out
/ReactAndroid/src/main/jni/prebuilt/lib/x86/
/ReactAndroid/src/main/gen
# Watchman
.watchmanconfig
# Android
.idea
.gradle
@@ -50,11 +47,6 @@ local.properties
node_modules
*.log
.nvm
/bots/node_modules/
# TODO: Check in yarn.lock in open source. Right now we need to keep it out
# from the GitHub repo as importing it might conflict with internal workspaces
*/**/yarn.lock
# OS X
.DS_Store
+53
View File
@@ -0,0 +1,53 @@
language: objective-c
osx_image: xcode8.2
install:
- mkdir -p /Users/travis/build/facebook/.nvm
- export NVM_DIR="/Users/travis/build/facebook/.nvm"
- brew install nvm
- source $(brew --prefix nvm)/nvm.sh
# TODO npm 2 started stalling on Travis, t11852928
# Use node 6 because that is what runs on land-blocking tests
- nvm install 6
- rm -Rf "${TMPDIR}/jest_preprocess_cache"
- wget https://github.com/yarnpkg/yarn/releases/download/v0.16.0/yarn-0.16.0.js
- export yarn="node $(pwd)/yarn-0.16.0.js"
- $yarn install
script:
- if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait ./scripts/objc-test-ios.sh test; fi
- if [[ "$TEST_TYPE" = objc-tvos ]]; then travis_retry travis_wait ./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 && ./scripts/process-podspecs.sh; fi
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:
- mkonicek@fb.com
- 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
-43
View File
@@ -1,43 +0,0 @@
# [Open Source Code of Conduct](https://code.facebook.com/codeofconduct)
This code of conduct outlines our expectations for participants within the **Facebook Open Source** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community.
Our open source community strives to:
* **Be friendly and patient.**
* **Be welcoming:** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
* **Be considerate:** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that were a world-wide community, so you might not be communicating in someone elses primary language.
* **Be respectful:** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a productive one.
* **Be careful in the words that you choose:** we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior arent acceptable. This includes, but is not limited to:
* Violent threats or language directed against another person.
* Discriminatory jokes and language.
* Posting sexually explicit or violent material.
* Posting (or threatening to post) other peoples personally identifying information (“doxing”).
* Personal insults, especially those using racist or sexist terms.
* Unwelcome sexual attention.
* Advocating for, or encouraging, any of the above behavior.
* Repeated harassment of others. In general, if someone asks you to stop, then stop.
* **When we disagree, try to understand why:** Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively.
* **Remember that were different.** The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesnt mean that theyre wrong. Dont forget that it is human to err and blaming each other doesnt get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
This code is not exhaustive or complete. It serves to distill our common understanding of a collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in the letter.
## Diversity Statement
We encourage everyone to participate and are committed to building a community for all. Although we may not be able to satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected characteristics above, including participants with disabilities.
## Reporting Issues
If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via opensource@fb.com. All reports will be handled with discretion. In your report please include:
* Your contact information.
* Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link.
* Any additional information that may be helpful.
After filing a report, a representative will contact you personally. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. A representative will then review the incident, follow up with any additional questions, and make a decision as to how to respond. We will respect confidentiality requests for the purpose of protecting victims of abuse.
Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our community without warning.
_This Code Of Conduct follows the [template](http://todogroup.org/opencodeofconduct/) established by the [TODO Group](http://todogroup.org/)._
+48 -131
View File
@@ -1,139 +1,44 @@
# Contributing to React Native
<!-- 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.
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 on [facebook.com](https://facebook.com). We're still working out the kinks to make contributing to this project as easy and transparent as possible, but we're not quite there yet. Hopefully this document makes the process for contributing clear and preempts some questions you may have.
## [Code of Conduct](https://code.facebook.com/codeofconduct)
## Our Development Process
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.
Some of the core team will be working directly on GitHub. 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.
## Get involved
### `master` is unsafe
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:
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 and version appropriately so you can lock into a specific version if need be.
* Simply start using React Native. Go through the [Getting Started](https://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](https://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](https://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](https://facebook.github.io/react-native/docs/contributing.html#your-first-pull-request). Issues tagged as [_Good first issue_](https://github.com/facebook/react-native/labels/Good%20first%20issue) are a good place to get started.
* Read through the [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html). If you find anything that is confusing or can be improved, you can make edits by clicking the "EDIT" button in the top-right corner 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.
### Pull Requests
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.
The core team will be monitoring for pull requests. When we get one, we'll run some Facebook-specific integration tests on it first. From here, we'll need to get another person to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process.
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.
**Please submit your pull request on the `master` branch**. If the fix is critical and should be included in a stable branch please mention it and it will be cherry picked into it by a project maintainer.
### Triaging issues and pull requests
*Before* submitting a pull request, please make sure the following is done…
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.
1. Fork the repo and create your branch from `master`.
2. **Describe your test plan in your commit.**
- 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.
* 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.
```
$ cd website
$ npm install && npm start
Open the following in your browser: http://localhost:8079/react-native/index.html
```
You can learn more about handling issues in the [maintainer's guide](docs/maintainers.html#handling-issues).
## Our development process
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.
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.
### Branch organization
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.
To see what changes are coming and provide better feedback to React Native contributors, use the [latest release candidate](https://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.
## Bugs
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](https://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues).
If you have questions about using React Native, the [Community page](https://facebook.github.io/react-native/help.html) list various resources that should help you get started.
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.
## Reporting new issues
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.
* **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`](https://facebook.github.io/react-native/versions.html). The bug may have already been fixed!
We're not able to provide support through GitHub Issues. If you're looking for help with your code, consider asking on [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native) or reaching out to the community through [other channels](https://facebook.github.io/react-native/support.html).
### Security bugs
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.
## Pull requests
### Your first pull request
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.
Working on your first Pull Request? You can learn how from this free video series:
[**How to Contribute to an Open Source Project on GitHub**](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
We have a list of [beginner friendly issues](https://github.com/facebook/react-native/labels/Good%20first%20issue) 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.
### Proposing a change
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 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.
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.
### Sending a pull request
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.
Please make sure the following is done when submitting a pull request:
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 pull request description. Make sure to [test your changes](https://facebook.github.io/react-native/docs/testing.html)!
4. Make sure your code lints (`npm run lint`).
5. If you haven't already, [sign the CLA](https://code.facebook.com/cla).
All pull requests should be opened against the `master` branch. After opening your pull request, ensure [**all tests pass**](https://facebook.github.io/react-native/docs/contributing.html#contrinuous-integration-tests) on Circle CI. If a test fails and you believe it is unrelated to your change, leave a comment on the pull request explaining why.
3. Add the copyright notice to the top of any new files you've added.
4. Ensure tests pass on Travis and Circle CI.
5. Make sure your code lints (`node linter.js <files touched>`).
6. If you haven't already, sign the [CLA](https://code.facebook.com/cla).
7. Squash your commits (`git rebase -i`).
One intent alongside one commit makes it clearer for people to review and easier to understand your intention.
> **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.
#### Test plan
A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI.
* If you've added code that should be tested, add tests!
* If you've changed APIs, update the documentation.
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 [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).
[circle]: https://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):
@@ -151,21 +56,34 @@ Copy and paste this to the top of your new file(s):
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)
### 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).
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, just let us know that you have completed the CLA and we can cross-check with your GitHub username.
### What happens next?
[Complete your CLA here](https://code.facebook.com/cla)
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.
## Bugs
### Where to Find Known Issues
We are using GitHub Issues for our public bugs. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make sure your problem doesn't already exist.
### Reporting New Issues
The best way to get your bug fixed is to provide a reduced test case. Please provide either a public repository with a runnable example or a [Sketch](https://sketch.expo.io/).
### Security Bugs
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.
## How to Get in Touch
* [Facebook](https://www.facebook.com/groups/react.native.community/)
* [Twitter](https://www.twitter.com/reactnative)
## 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
### Code
#### General
@@ -177,7 +95,7 @@ However, there are still some styles that the linter cannot pick up.
#### JavaScript
* Use semicolons;
* ES6 standards
* `'use strict';`
* Prefer `'` over `"`
* Do not use the optional parameters of `setTimeout` and `setInterval`
* 80 character line length
@@ -211,4 +129,3 @@ However, there are still some styles that the linter cannot pick up.
## License
By contributing to React Native, you agree that your contributions will be licensed under its BSD license.
<!-- generated_contributing_end -->
+7 -2
View File
@@ -1,8 +1,9 @@
FROM hramos/android-base:latest
FROM containership/android-base:latest
# set default environment variables
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
ENV GRADLE_OPTS="-Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
ENV REACT_NATIVE_MAX_WORKERS=1
# add ReactAndroid directory
ADD .buckconfig /app/.buckconfig
@@ -46,5 +47,9 @@ RUN cd $(npm root -g)/npm && npm install fs-extra && sed -i -e s/graceful-fs/fs-
# build node dependencies
RUN npm install
RUN npm install github@0.2.4
WORKDIR /app/website
RUN npm install
WORKDIR /app
+36 -12
View File
@@ -1,8 +1,8 @@
FROM library/ubuntu:16.04
# set default build arguments
ARG ANDROID_TOOLS_VERSION=25.2.5
ARG BUCK_VERSION=v2017.11.16.01
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
@@ -12,7 +12,7 @@ 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}/tools/bin:${ANDROID_HOME}/platform-tools
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}
@@ -28,8 +28,9 @@ RUN npm install n -g
RUN n $NODE_VERSION
# download buck
RUN git clone https://github.com/facebook/buck.git /opt/buck --branch $BUCK_VERSION --depth=1
RUN git clone https://github.com/facebook/buck.git /opt/buck
WORKDIR /opt/buck
RUN git checkout $BUCK_VERSION
# build buck
RUN ant
@@ -45,11 +46,10 @@ RUN ./configure
RUN make
RUN make install
# Full reference at https://dl.google.com/android/repository/repository2-1.xml
# download and unpack android
RUN mkdir /opt/android
WORKDIR /opt/android
RUN curl --silent https://dl.google.com/android/repository/tools_r$ANDROID_TOOLS_VERSION-linux.zip > android.zip
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
@@ -63,15 +63,39 @@ RUN unzip ndk.zip
RUN rm ndk.zip
# Add android SDK tools
RUN echo "y" | sdkmanager "system-images;android-19;google_apis;armeabi-v7a"
RUN echo "y" | sdkmanager "platforms;android-23"
RUN echo "y" | sdkmanager "platforms;android-19"
RUN echo "y" | sdkmanager "build-tools;23.0.1"
RUN echo "y" | sdkmanager "add-ons;addon-google_apis-google-23"
RUN echo "y" | sdkmanager "extras;android;m2repository"
# 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
+1 -1
View File
@@ -1,6 +1,6 @@
FROM library/node:6.9.2
ENV YARN_VERSION=0.27.5
ENV YARN_VERSION=0.19.1
# install dependencies
RUN apt-get update && apt-get install ocaml libelf-dev -y
@@ -20,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');
@@ -44,7 +45,7 @@ const test_opts = {
OFFSET: argv.offset,
COUNT: argv.count
};
}
let max_test_class_length = Number.NEGATIVE_INFINITY;
@@ -84,14 +85,14 @@ if (test_opts.COUNT != null && test_opts.OFFSET != null) {
}
return async.mapSeries(testClasses, (clazz, callback) => {
if (clazz.length > max_test_class_length) {
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('./ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh', [test_opts.PACKAGE, clazz], {
stdio: 'inherit'
});
})
const timeout = setTimeout(() => {
test_process.kill();
@@ -105,7 +106,7 @@ return 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}`));
}
@@ -137,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++;
}
+3 -2
View File
@@ -17,6 +17,7 @@ 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
REACT_NATIVE_MAX_WORKERS=1
# solve issue with max user watches limit
echo 65536 | tee -a /proc/sys/fs/inotify/max_user_watches
@@ -230,13 +231,13 @@ function e2e_suite() {
# 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
REACT_NATIVE_MAX_WORKERS=1 react-native bundle --platform android --dev true --entry-file index.android.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
REACT_NATIVE_MAX_WORKERS=1 react-native bundle --platform ios --dev true --entry-file index.ios.js --bundle-output ios-bundle.js
if [ $? -ne 0 ]; then
echo "Could not build iOS bundle"
return 1
@@ -29,7 +29,7 @@ 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
@@ -1,52 +0,0 @@
/**
* 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.
*
* @flow
* @providesModule AccessibilityManagerTest
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const { View } = ReactNative;
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
const {
TestModule,
AccessibilityManager,
} = ReactNative.NativeModules;
class AccessibilityManagerTest extends React.Component<{}> {
componentDidMount() {
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,
});
RCTDeviceEventEmitter.addListener('didUpdateDimensions', update => {
TestModule.markTestPassed(update.window.fontScale === 4.0);
});
}
render(): React.Node {
return <View />;
}
}
AccessibilityManagerTest.displayName = 'AccessibilityManagerTest';
module.exports = AccessibilityManagerTest;
+1 -1
View File
@@ -32,7 +32,7 @@ type State = {
elapsed?: string,
};
class AppEventsTest extends React.Component<{}, State> {
class AppEventsTest extends React.Component {
state: State = {sent: 'none', received: 'none'};
componentDidMount() {
+3 -7
View File
@@ -155,7 +155,7 @@ function testOptimizedMultiGet() {
let keys = batch.map(([key, value]) => key);
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);
@@ -168,7 +168,7 @@ function testOptimizedMultiGet() {
}
class AsyncStorageTest extends React.Component<{}, $FlowFixMeState> {
class AsyncStorageTest extends React.Component {
state = {
messages: 'Initializing...',
done: false,
@@ -189,11 +189,7 @@ class AsyncStorageTest extends React.Component<{}, $FlowFixMeState> {
return (
<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>
+6 -6
View File
@@ -41,14 +41,14 @@ type State = {
'force-cache'?: boolean,
}
class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
class ImageCachePolicyTest extends React.Component {
state = {}
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);
}
@@ -65,7 +65,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
<Text>Hello</Text>
<Image
source={{
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
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)}
@@ -74,7 +74,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
/>
<Image
source={{
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'default'
}}
onLoad={() => this.testComplete('default', true)}
@@ -83,7 +83,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
/>
<Image
source={{
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'reload'
}}
onLoad={() => this.testComplete('reload', true)}
@@ -92,7 +92,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
/>
<Image
source={{
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
cache: 'force-cache'
}}
onLoad={() => this.testComplete('force-cache', true)}
+1 -1
View File
@@ -19,7 +19,7 @@ var {
} = 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.');
+7 -12
View File
@@ -11,9 +11,6 @@
*/
'use strict';
/* $FlowFixMe(>=0.54.0 site=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. */
var requestAnimationFrame = require('fbjs/lib/requestAnimationFrame');
var React = require('react');
var PropTypes = require('prop-types');
@@ -24,10 +21,12 @@ var {
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
class IntegrationTestHarnessTest extends React.Component<{
shouldThrow?: boolean,
waitOneFrame?: boolean,
}, $FlowFixMeState> {
class IntegrationTestHarnessTest extends React.Component {
props: {
shouldThrow?: boolean,
waitOneFrame?: boolean,
};
static propTypes = {
shouldThrow: PropTypes.bool,
waitOneFrame: PropTypes.bool,
@@ -63,11 +62,7 @@ class IntegrationTestHarnessTest extends React.Component<{
return (
<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>
+1 -9
View File
@@ -33,16 +33,11 @@ var TESTS = [
require('./ImageCachePolicyTest'),
require('./ImageSnapshotTest'),
require('./PromiseTest'),
require('./WebViewTest'),
require('./SyncMethodTest'),
require('./WebSocketTest'),
require('./AccessibilityManagerTest'),
];
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)
);
@@ -51,7 +46,7 @@ require('LoggingTestModule');
type Test = any;
class IntegrationTestsApp extends React.Component<{}, $FlowFixMeState> {
class IntegrationTestsApp extends React.Component {
state = {
test: (null: ?Test),
};
@@ -60,9 +55,6 @@ class IntegrationTestsApp extends React.Component<{}, $FlowFixMeState> {
if (this.state.test) {
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. */}
<this.state.test />
</ScrollView>
);
+13 -5
View File
@@ -12,7 +12,6 @@
'use strict';
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var {
Image,
@@ -25,11 +24,21 @@ var { TestModule } = ReactNative.NativeModules;
var deepDiffer = require('deepDiffer');
function debug(...args) {
function debug() {
// console.log.apply(null, arguments);
}
import type {Layout, LayoutEvent} from 'CoreEventTypes';
type Layout = {
x: number;
y: number;
width: number;
height: number;
};
type LayoutEvent = {
nativeEvent: {
layout: Layout;
};
};
type Style = {
margin?: number,
padding?: number,
@@ -49,8 +58,7 @@ type State = {
containerStyle?: Style,
};
var LayoutEventsTest = createReactClass({
displayName: 'LayoutEventsTest',
var LayoutEventsTest = React.createClass({
getInitialState(): State {
return {
didAnimation: false,
+2 -2
View File
@@ -16,7 +16,7 @@ var ReactNative = require('react-native');
var { View } = ReactNative;
var { TestModule } = ReactNative.NativeModules;
class PromiseTest extends React.Component<{}> {
class PromiseTest extends React.Component {
shouldResolve = false;
shouldReject = false;
shouldSucceedAsync = false;
@@ -66,7 +66,7 @@ class PromiseTest extends React.Component<{}> {
}
};
render(): React.Node {
render(): React.Element<any> {
return <View />;
}
}
@@ -10,7 +10,6 @@
'use strict';
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var RCTNativeAppEventEmitter = require('RCTNativeAppEventEmitter');
var Subscribable = require('Subscribable');
@@ -25,12 +24,11 @@ var reactViewHeight = 102;
var newReactViewWidth = 201;
var newReactViewHeight = 202;
var ReactContentSizeUpdateTest = createReactClass({
displayName: 'ReactContentSizeUpdateTest',
var ReactContentSizeUpdateTest = React.createClass({
mixins: [Subscribable.Mixin,
TimerMixin],
UNSAFE_componentWillMount: function() {
componentWillMount: function() {
this.addListenerOn(
RCTNativeAppEventEmitter,
'rootViewDidChangeIntrinsicSize',
+1 -4
View File
@@ -13,9 +13,6 @@
var React = require('react');
var ReactNative = require('react-native');
/* $FlowFixMe(>=0.54.0 site=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. */
var requestAnimationFrame = require('fbjs/lib/requestAnimationFrame');
var {
@@ -24,7 +21,7 @@ var {
} = 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.');
@@ -10,7 +10,6 @@
'use strict';
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var RCTNativeAppEventEmitter = require('RCTNativeAppEventEmitter');
var Subscribable = require('Subscribable');
@@ -23,11 +22,10 @@ var reactViewHeight = 222;
var finalState = false;
var SizeFlexibilityUpdateTest = createReactClass({
displayName: 'SizeFlexibilityUpdateTest',
var SizeFlexibilityUpdateTest = React.createClass({
mixins: [Subscribable.Mixin],
UNSAFE_componentWillMount: function() {
componentWillMount: function() {
this.addListenerOn(
RCTNativeAppEventEmitter,
'rootViewDidChangeIntrinsicSize',
+2 -2
View File
@@ -21,7 +21,7 @@ const {
} = 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 sync method export');
@@ -32,7 +32,7 @@ class SyncMethodTest extends React.Component<{}> {
TestModule.markTestCompleted();
}
render(): React.Node {
render(): React.Element<any> {
return <View />;
}
}
+1 -6
View File
@@ -12,11 +12,7 @@
'use strict';
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
/* $FlowFixMe(>=0.54.0 site=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. */
var TimerMixin = require('react-timer-mixin');
var {
@@ -26,8 +22,7 @@ var {
} = ReactNative;
var { TestModule } = ReactNative.NativeModules;
var TimersTest = createReactClass({
displayName: 'TimersTest',
var TimersTest = React.createClass({
mixins: [TimerMixin],
_nextTest: () => {},
+2 -2
View File
@@ -42,7 +42,7 @@ type State = {
testExpectedResponse: string;
};
class WebSocketTest extends React.Component<{}, State> {
class WebSocketTest extends React.Component {
state: State = {
url: DEFAULT_WS_URL,
fetchStatus: null,
@@ -158,7 +158,7 @@ class WebSocketTest extends React.Component<{}, State> {
});
}
render(): React.Node {
render(): React.Element<any> {
return <View />;
}
}
-61
View File
@@ -1,61 +0,0 @@
/**
* 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;
@@ -15,9 +15,6 @@
/* eslint-env node */
/* $FlowFixMe(>=0.54.0 site=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. */
const WebSocket = require('ws');
console.log(`\
+13
View File
@@ -0,0 +1,13 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#import "JSContextRef.h"
extern "C" {
void nativeProfilerEnableBytecode(void);
void nativeProfilerStart(JSContextRef ctx, const char *title);
void nativeProfilerEnd(JSContextRef ctx, const char *title, const char *filename);
}
+311
View File
@@ -0,0 +1,311 @@
#include "JSCLegacyProfiler.h"
#include "APICast.h"
#include "LegacyProfiler.h"
#include "OpaqueJSString.h"
#include "JSProfilerPrivate.h"
#include "JSStringRef.h"
#include "String.h"
#include "Options.h"
enum json_gen_status {
json_gen_status_ok = 0,
json_gen_status_error = 1,
};
enum json_entry {
json_entry_key,
json_entry_value,
};
namespace {
struct json_state {
FILE *fileOut;
bool hasFirst;
};
}
typedef json_state *json_gen;
static void json_escaped_cstring_printf(json_gen gen, const char *str) {
const char *cursor = str;
fputc('"', gen->fileOut);
while (*cursor) {
const char *escape = nullptr;
switch (*cursor) {
case '"':
escape = "\\\"";
break;
case '\b':
escape = "\\b";
break;
case '\f':
escape = "\\f";
break;
case '\n':
escape = "\\n";
break;
case '\r':
escape = "\\r";
break;
case '\t':
escape = "\\t";
break;
case '\\':
escape = "\\\\";
break;
default:
break;
}
if (escape != nullptr) {
fwrite(escape, 1, strlen(escape), gen->fileOut);
} else {
fputc(*cursor, gen->fileOut);
}
cursor++;
}
fputc('"', gen->fileOut);
}
static json_gen_status json_gen_key_cstring(json_gen gen, const char *buffer) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
if (gen->hasFirst) {
fprintf(gen->fileOut, ",");
}
gen->hasFirst = true;
json_escaped_cstring_printf(gen, buffer);
return json_gen_status_ok;
}
static json_gen_status json_gen_map_open(json_gen gen, json_entry entryType) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
if (entryType == json_entry_value) {
fprintf(gen->fileOut, ":");
} else if (entryType == json_entry_key) {
if (gen->hasFirst) {
fprintf(gen->fileOut, ",");
}
}
fprintf(gen->fileOut, "{");
gen->hasFirst = false;
return json_gen_status_ok;
}
static json_gen_status json_gen_map_close(json_gen gen) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
fprintf(gen->fileOut, "}");
gen->hasFirst = true;
return json_gen_status_ok;
}
static json_gen_status json_gen_array_open(json_gen gen, json_entry entryType) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
if (entryType == json_entry_value) {
fprintf(gen->fileOut, ":");
} else if (entryType == json_entry_key) {
if (gen->hasFirst) {
fprintf(gen->fileOut, ",");
}
}
fprintf(gen->fileOut, "[");
gen->hasFirst = false;
return json_gen_status_ok;
}
static json_gen_status json_gen_array_close(json_gen gen) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
fprintf(gen->fileOut, "]");
gen->hasFirst = true;
return json_gen_status_ok;
}
static json_gen_status json_gen_keyvalue_cstring(json_gen gen, const char *key, const char *value) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
if (gen->hasFirst) {
fprintf(gen->fileOut, ",");
}
gen->hasFirst = true;
fprintf(gen->fileOut, "\"%s\" : ", key);
json_escaped_cstring_printf(gen, value);
return json_gen_status_ok;
}
static json_gen_status json_gen_keyvalue_integer(json_gen gen, const char *key, int value) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
if (gen->hasFirst) {
fprintf(gen->fileOut, ",");
}
gen->hasFirst = true;
fprintf(gen->fileOut, "\"%s\": %d", key, value);
return json_gen_status_ok;
}
static json_gen_status json_gen_keyvalue_double(json_gen gen, const char *key, double value) {
if (gen->fileOut == nullptr) {
return json_gen_status_error;
}
if (gen->hasFirst) {
fprintf(gen->fileOut, ",");
}
gen->hasFirst = true;
fprintf(gen->fileOut, "\"%s\": %.20g", key, value);
return json_gen_status_ok;
}
static json_gen json_gen_alloc(const char *fileName) {
json_gen gen = (json_gen)malloc(sizeof(json_state));
memset(gen, 0, sizeof(json_state));
gen->fileOut = fopen(fileName, "wb");
return gen;
}
static void json_gen_free(json_gen gen) {
if (gen->fileOut) {
fclose(gen->fileOut);
}
free(gen);
}
#define GEN_AND_CHECK(expr) \
do { \
json_gen_status GEN_AND_CHECK_status = (expr); \
if (GEN_AND_CHECK_status != json_gen_status_ok) { \
return GEN_AND_CHECK_status; \
} \
} while (false)
static json_gen_status append_children_array_json(json_gen gen, const JSC::ProfileNode *node);
static json_gen_status append_node_json(json_gen gen, const JSC::ProfileNode *node);
static json_gen_status append_root_json(json_gen gen, const JSC::Profile *profile) {
GEN_AND_CHECK(json_gen_map_open(gen, json_entry_key));
GEN_AND_CHECK(json_gen_key_cstring(gen, "rootNodes"));
#if IOS8
GEN_AND_CHECK(append_children_array_json(gen, profile->head()));
#else
GEN_AND_CHECK(append_children_array_json(gen, profile->rootNode()));
#endif
GEN_AND_CHECK(json_gen_map_close(gen));
return json_gen_status_ok;
}
static json_gen_status append_children_array_json(json_gen gen, const JSC::ProfileNode *node) {
GEN_AND_CHECK(json_gen_array_open(gen, json_entry_value));
for (RefPtr<JSC::ProfileNode> child : node->children()) {
GEN_AND_CHECK(append_node_json(gen, child.get()));
}
GEN_AND_CHECK(json_gen_array_close(gen));
return json_gen_status_ok;
}
static json_gen_status append_node_json(json_gen gen, const JSC::ProfileNode *node) {
GEN_AND_CHECK(json_gen_map_open(gen, json_entry_key));
GEN_AND_CHECK(json_gen_keyvalue_integer(gen, "id", node->id()));
if (!node->functionName().isEmpty()) {
GEN_AND_CHECK(json_gen_keyvalue_cstring(gen, "functionName", node->functionName().utf8().data()));
}
if (!node->url().isEmpty()) {
GEN_AND_CHECK(json_gen_keyvalue_cstring(gen, "url", node->url().utf8().data()));
GEN_AND_CHECK(json_gen_keyvalue_integer(gen, "lineNumber", node->lineNumber()));
GEN_AND_CHECK(json_gen_keyvalue_integer(gen, "columnNumber", node->columnNumber()));
}
GEN_AND_CHECK(json_gen_key_cstring(gen, "calls"));
GEN_AND_CHECK(json_gen_array_open(gen, json_entry_value));
for (const JSC::ProfileNode::Call &call : node->calls()) {
GEN_AND_CHECK(json_gen_map_open(gen, json_entry_key));
GEN_AND_CHECK(json_gen_keyvalue_double(gen, "startTime", call.startTime()));
#if IOS8
GEN_AND_CHECK(json_gen_keyvalue_double(gen, "totalTime", call.totalTime()));
#else
GEN_AND_CHECK(json_gen_keyvalue_double(gen, "totalTime", call.elapsedTime()));
#endif
GEN_AND_CHECK(json_gen_map_close(gen));
}
GEN_AND_CHECK(json_gen_array_close(gen));
if (!node->children().isEmpty()) {
GEN_AND_CHECK(json_gen_key_cstring(gen, "children"));
GEN_AND_CHECK(append_children_array_json(gen, node));
}
GEN_AND_CHECK(json_gen_map_close(gen));
return json_gen_status_ok;
}
static void convert_to_json(const JSC::Profile *profile, const char *filename) {
json_gen_status status;
json_gen gen = json_gen_alloc(filename);
status = append_root_json(gen, profile);
if (status != json_gen_status_ok) {
FILE *fileOut = fopen(filename, "wb");
if (fileOut != nullptr) {
fprintf(fileOut, "{\"error\": %d}", (int)status);
fclose(fileOut);
}
}
json_gen_free(gen);
}
// Based on JSEndProfiling, with a little extra code to return the profile as JSON.
static void JSEndProfilingAndRender(JSContextRef ctx, const char *title, const char *filename)
{
JSC::ExecState *exec = toJS(ctx);
JSC::LegacyProfiler *profiler = JSC::LegacyProfiler::profiler();
RefPtr<JSC::Profile> rawProfile = profiler->stopProfiling(exec, WTF::String(title));
convert_to_json(rawProfile.get(), filename);
}
extern "C" {
void nativeProfilerEnableBytecode(void)
{
JSC::Options::setOption("forceProfilerBytecodeGeneration=true");
}
void nativeProfilerStart(JSContextRef ctx, const char *title) {
JSStartProfiling(ctx, JSStringCreateWithUTF8CString(title));
}
void nativeProfilerEnd(JSContextRef ctx, const char *title, const char *filename) {
JSEndProfilingAndRender(ctx, title, filename);
}
}
+18
View File
@@ -0,0 +1,18 @@
ios9:
IOS_VERSION=9 \
JSC_VERSION=7601.1.46.3 \
WEB_CORE_VERSION=7601.1.46.10 \
WTF_VERSION=7601.1.46.3 \
make -f Makefile.base
ios8:
IOS_VERSION=8 \
JSC_VERSION=7600.1.17 \
WEB_CORE_VERSION=7600.1.25 \
WTF_VERSION=7600.1.24 \
make -f Makefile.base
.PHONY: clean
clean:
-rm -rf $(wildcard *.dylib)
-rm -rf download
+80
View File
@@ -0,0 +1,80 @@
HEADER_PATHS := `find download/JavaScriptCore/JavaScriptCore-$(JSC_VERSION) -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"`
XCODE_PATH ?= $(shell xcode-select -p)
SDK_PATH = $(XCODE_PATH)/Platforms/$1.platform/Developer/SDKs/$1.sdk
SDK_VERSION = $(shell plutil -convert json -o - $(call SDK_PATH,iPhoneOS)/SDKSettings.plist | awk -f parseSDKVersion.awk)
CERT ?= iPhone Developer
ARCHS = x86_64 arm64 armv7 i386
PLATFORM = \
if [[ "$*" = "x86_64" || "$*" = "i386" ]]; then \
PLATFORM=iPhoneSimulator; \
else \
PLATFORM=iPhoneOS; \
fi;
SYSROOT = -isysroot $(call SDK_PATH,$${PLATFORM})
IOS_LIBS = \
download/JavaScriptCore/JavaScriptCore-$(JSC_VERSION) \
download/WebCore/WebCore-$(WEB_CORE_VERSION) \
download/WTF/WTF-$(WTF_VERSION) \
download/JavaScriptCore/JavaScriptCore-$(JSC_VERSION)/Bytecodes.h
IOS_EXT=ios$(IOS_VERSION)
ifneq ($(SDK_VERSION), $(IOS_VERSION))
all:
$(error "Expected to be compiled with iOS SDK version 8, found $(SDK_VERSION)")
else
all: RCTJSCProfiler.$(IOS_EXT).dylib /tmp/RCTJSCProfiler
cp $^
endif
/tmp/RCTJSCProfiler:
mkdir -p $@
RCTJSCProfiler.$(IOS_EXT).dylib: RCTJSCProfiler_unsigned.$(IOS_EXT).dylib
cp $< $@
codesign -f -s "${CERT}" $@
.PRECIOUS: RCTJSCProfiler_unsigned.$(IOS_EXT).dylib
RCTJSCProfiler_unsigned.$(IOS_EXT).dylib: $(patsubst %,RCTJSCProfiler_%.$(IOS_EXT).dylib,$(ARCHS))
lipo -create -output $@ $^
.PRECIOUS: RCTJSCProfiler_%.$(IOS_EXT).dylib
RCTJSCProfiler_%.$(IOS_EXT).dylib: $(IOS_LIBS)
$(PLATFORM) \
clang -w -dynamiclib -o RCTJSCProfiler_$*.$(IOS_EXT).dylib -std=c++11 \
-arch $* \
-install_name RCTJSCProfiler.$(IOS_EXT).dylib \
-include ./download/JavaScriptCore/JavaScriptCore-$(JSC_VERSION)/config.h \
-I download \
-I download/WebCore/WebCore-$(WEB_CORE_VERSION)/icu \
-I download/WTF/WTF-$(WTF_VERSION) \
-DNDEBUG=1 \
-DIOS$(IOS_VERSION)=1 \
-miphoneos-version-min=8.0 \
$(SYSROOT) \
$(HEADER_PATHS) \
-undefined dynamic_lookup \
JSCLegacyProfiler.mm
.PRECIOUS: %/Bytecodes.h
%/Bytecodes.h:
python $*/generate-bytecode-files --bytecodes_h $@ $*/bytecode/BytecodeList.json
.PRECIOUS: download/%
download/%: download/%.tar.gz
tar -zxvf $< -C `dirname $@` > /dev/null
.PRECIOUS: %.tar.gz
%.tar.gz:
mkdir -p `dirname $@`
curl -o $@ http://www.opensource.apple.com/tarballs/$(patsubst download/%,%,$@)
+254
View File
@@ -0,0 +1,254 @@
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import argparse
import json
import smap
import trace_data
import urllib
SECONDS_TO_NANOSECONDS = (1000*1000)
SAMPLE_DELTA_IN_SECONDS = 0.0001
class Marker(object):
def __init__(self, _name, _timestamp, _depth, _is_end, _ident, url, line, col):
self.name = _name
self.timestamp = _timestamp
self.depth = _depth
self.is_end = _is_end
self.ident = _ident
self.url = url
self.line = line
self.col = col
# sort markers making sure they are ordered by timestamp then depth of function call
# and finally that markers of the same ident are sorted in the order begin then end
def __cmp__(self, other):
if self.timestamp < other.timestamp:
return -1
if self.timestamp > other.timestamp:
return 1
if self.depth < other.depth:
return -1
if self.depth > other.depth:
return 1
if self.ident == other.ident:
if self.is_end:
return 1
return 0
# calculate marker name based on combination of function name and location
def _calcname(entry):
funcname = ""
if "functionName" in entry:
funcname = funcname + entry["functionName"]
return funcname
def _calcurl(mapcache, entry, map_file):
if entry.url not in mapcache:
map_url = entry.url.replace('.bundle', '.map')
if map_url != entry.url:
if map_file:
print('Loading sourcemap from:' + map_file)
map_url = map_file
try:
url_file = urllib.urlopen(map_url)
if url_file != None:
entries = smap.parse(url_file)
mapcache[entry.url] = entries
except Exception, e:
mapcache[entry.url] = []
if entry.url in mapcache:
source_entry = smap.find(mapcache[entry.url], entry.line, entry.col)
if source_entry:
entry.url = 'file://' + source_entry.src
entry.line = source_entry.src_line
entry.col = source_entry.src_col
def _compute_markers(markers, call_point, depth):
name = _calcname(call_point)
ident = len(markers)
url = ""
lineNumber = -1
columnNumber = -1
if "url" in call_point:
url = call_point["url"]
if "lineNumber" in call_point:
lineNumber = call_point["lineNumber"]
if "columnNumber" in call_point:
columnNumber = call_point["columnNumber"]
for call in call_point["calls"]:
markers.append(Marker(name, call["startTime"], depth, 0, ident, url, lineNumber, columnNumber))
markers.append(Marker(name, call["startTime"] + call["totalTime"], depth, 1, ident, url, lineNumber, columnNumber))
ident = ident + 2
if "children" in call_point:
for child in call_point["children"]:
_compute_markers(markers, child, depth+1);
def _find_child(children, name):
for child in children:
if child['functionName'] == name:
return child
return None
def _add_entry_cpuprofiler_program(newtime, cpuprofiler):
curnode = _find_child(cpuprofiler['head']['children'], '(program)')
if cpuprofiler['lastTime'] != None:
lastTime = cpuprofiler['lastTime']
while lastTime < newtime:
curnode['hitCount'] += 1
cpuprofiler['samples'].append(curnode['callUID'])
cpuprofiler['timestamps'].append(int(lastTime*SECONDS_TO_NANOSECONDS))
lastTime += SAMPLE_DELTA_IN_SECONDS
cpuprofiler['lastTime'] = lastTime
else:
cpuprofiler['lastTime'] = newtime
def _add_entry_cpuprofiler(stack, newtime, cpuprofiler):
index = len(stack) - 1
marker = stack[index]
if marker.name not in cpuprofiler['markers']:
cpuprofiler['markers'][marker.name] = cpuprofiler['id']
cpuprofiler['callUID'] += 1
callUID = cpuprofiler['markers'][marker.name]
curnode = cpuprofiler['head']
index = 0
while index < len(stack):
newnode = _find_child(curnode['children'], stack[index].name)
if newnode == None:
newnode = {}
newnode['callUID'] = callUID
newnode['url'] = marker.url
newnode['functionName'] = stack[index].name
newnode['hitCount'] = 0
newnode['lineNumber'] = marker.line
newnode['columnNumber'] = marker.col
newnode['scriptId'] = callUID
newnode['positionTicks'] = []
newnode['id'] = cpuprofiler['id']
cpuprofiler['id'] += 1
newnode['children'] = []
curnode['children'].append(newnode)
curnode['deoptReason'] = ''
curnode = newnode
index += 1
if cpuprofiler['lastTime'] == None:
cpuprofiler['lastTime'] = newtime
if cpuprofiler['lastTime'] != None:
lastTime = cpuprofiler['lastTime']
while lastTime < newtime:
curnode['hitCount'] += 1
if len(curnode['positionTicks']) == 0:
ticks = {}
ticks['line'] = curnode['callUID']
ticks['ticks'] = 0
curnode['positionTicks'].append(ticks)
curnode['positionTicks'][0]['ticks'] += 1
cpuprofiler['samples'].append(curnode['callUID'])
cpuprofiler['timestamps'].append(int(lastTime*1000*1000))
lastTime += 0.0001
cpuprofiler['lastTime'] = lastTime
def _create_default_cpuprofiler_node(name, _id, _uid):
return {'functionName': name,
'scriptId':'0',
'url':'',
'lineNumber':0,
'columnNumber':0,
'positionTicks':[],
'id':_id,
'callUID':_uid,
'children': [],
'hitCount': 0,
'deoptReason':''}
def main():
parser = argparse.ArgumentParser(description="Converts JSON profile format to fbsystrace text output")
parser.add_argument(
"-o",
dest = "output_file",
default = None,
help = "Output file for trace data")
parser.add_argument(
"-cpuprofiler",
dest = "output_cpuprofiler",
default = None,
help = "Output file for cpuprofiler data")
parser.add_argument(
"-map",
dest = "map_file",
default = None,
help = "Map file for symbolicating")
parser.add_argument( "file", help = "JSON trace input_file")
args = parser.parse_args()
markers = []
with open(args.file, "r") as trace_file:
trace = json.load(trace_file)
for root_entry in trace["rootNodes"]:
_compute_markers(markers, root_entry, 0)
mapcache = {}
for m in markers:
_calcurl(mapcache, m, args.map_file)
sorted_markers = list(sorted(markers));
if args.output_cpuprofiler != None:
cpuprofiler = {}
cpuprofiler['startTime'] = None
cpuprofiler['endTime'] = None
cpuprofiler['lastTime'] = None
cpuprofiler['id'] = 4
cpuprofiler['callUID'] = 4
cpuprofiler['samples'] = []
cpuprofiler['timestamps'] = []
cpuprofiler['markers'] = {}
cpuprofiler['head'] = _create_default_cpuprofiler_node('(root)', 1, 1)
cpuprofiler['head']['children'].append(_create_default_cpuprofiler_node('(root)', 2, 2))
cpuprofiler['head']['children'].append(_create_default_cpuprofiler_node('(program)', 3, 3))
marker_stack = []
with open(args.output_cpuprofiler, 'w') as file_out:
for marker in sorted_markers:
if len(marker_stack):
_add_entry_cpuprofiler(marker_stack, marker.timestamp, cpuprofiler)
else:
_add_entry_cpuprofiler_program(marker.timestamp, cpuprofiler)
if marker.is_end:
marker_stack.pop()
else:
marker_stack.append(marker)
cpuprofiler['startTime'] = cpuprofiler['timestamps'][0] / 1000000.0
cpuprofiler['endTime'] = cpuprofiler['timestamps'][len(cpuprofiler['timestamps']) - 1] / 1000000.0
json.dump(cpuprofiler, file_out, sort_keys=False, indent=4, separators=(',', ': '))
if args.output_file != None:
with open(args.output_file,"w") as trace_file:
for marker in sorted_markers:
start_or_end = None
if marker.is_end:
start_or_end = "E"
else:
start_or_end = "B"
#output with timestamp at high level of precision
trace_file.write("json-0 [000] .... {0:.12f}: tracing_mark_write: {1}|0|{2}\n".format(
marker.timestamp,
start_or_end,
marker.name))
main()
+10
View File
@@ -0,0 +1,10 @@
BEGIN {
FS = ":"
RS = ","
}
/"Version"/ {
version = substr($2, 2, length($2) - 2)
print int(version)
exit 0
}
+343
View File
@@ -0,0 +1,343 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
"""
adapted from https://github.com/martine/python-sourcemap into a reuasable module
"""
"""
Apache License
Version 2.0, January 2010
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
"""A module for parsing source maps, as output by the Closure and
CoffeeScript compilers and consumed by browsers. See
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/
"""
import collections
import json
import sys
import bisect
class entry(object):
def __init__(self, dst_line, dst_col, src, src_line, src_col):
self.dst_line = dst_line
self.dst_col = dst_col
self.src = src
self.src_line = src_line
self.src_col = src_col
def __cmp__(self, other):
#print(self)
#print(other)
if self.dst_line < other.dst_line:
return -1
if self.dst_line > other.dst_line:
return 1
if self.dst_col < other.dst_col:
return -1
if self.dst_col > other.dst_col:
return 1
return 0
SmapState = collections.namedtuple(
'SmapState', ['dst_line', 'dst_col',
'src', 'src_line', 'src_col',
'name'])
# Mapping of base64 letter -> integer value.
B64 = dict((c, i) for i, c in
enumerate('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
'0123456789+/'))
def _parse_vlq(segment):
"""Parse a string of VLQ-encoded data.
Returns:
a list of integers.
"""
values = []
cur, shift = 0, 0
for c in segment:
val = B64[c]
# Each character is 6 bits:
# 5 of value and the high bit is the continuation.
val, cont = val & 0b11111, val >> 5
cur += val << shift
shift += 5
if not cont:
# The low bit of the unpacked value is the sign.
cur, sign = cur >> 1, cur & 1
if sign:
cur = -cur
values.append(cur)
cur, shift = 0, 0
if cur or shift:
raise Exception('leftover cur/shift in vlq decode')
return values
def _parse_smap(file):
"""Given a file-like object, yield SmapState()s as they are read from it."""
smap = json.load(file)
sources = smap['sources']
names = smap['names']
mappings = smap['mappings']
lines = mappings.split(';')
dst_col, src_id, src_line, src_col, name_id = 0, 0, 0, 0, 0
for dst_line, line in enumerate(lines):
segments = line.split(',')
dst_col = 0
for segment in segments:
if not segment:
continue
parsed = _parse_vlq(segment)
dst_col += parsed[0]
src = None
name = None
if len(parsed) > 1:
src_id += parsed[1]
src = sources[src_id]
src_line += parsed[2]
src_col += parsed[3]
if len(parsed) > 4:
name_id += parsed[4]
name = names[name_id]
assert dst_line >= 0
assert dst_col >= 0
assert src_line >= 0
assert src_col >= 0
yield SmapState(dst_line, dst_col, src, src_line, src_col, name)
def find(entries, line, col):
test = entry(line, col, '', 0, 0)
index = bisect.bisect_right(entries, test)
if index == 0:
return None
return entries[index - 1]
def parse(file):
# Simple demo that shows files that most contribute to total size.
lookup = []
for state in _parse_smap(file):
lookup.append(entry(state.dst_line, state.dst_col, state.src, state.src_line, state.src_col))
sorted_lookup = list(sorted(lookup))
return sorted_lookup
+244
View File
@@ -0,0 +1,244 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import re
import unittest
"""
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
<idle>-0 [001] ...2 3269.291072: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=mmcqd/0 next_pid=120 next_prio=120
"""
TRACE_LINE_PATTERN = re.compile(
r'^\s*(?P<task>.+)-(?P<pid>\d+)\s+(?:\((?P<tgid>.+)\)\s+)?\[(?P<cpu>\d+)\]\s+(?:(?P<flags>\S{4})\s+)?(?P<timestamp>[0-9.]+):\s+(?P<function>.+)$')
"""
Example lines from custom app traces:
0: B|27295|providerRemove
0: E
tracing_mark_write: S|27311|NNFColdStart<D-7744962>|1112249168
"""
APP_TRACE_LINE_PATTERN = re.compile(
r'^(?P<type>.+?): (?P<args>.+)$')
"""
Example section names:
NNFColdStart
NNFColdStart<0><T7744962>
NNFColdStart<X>
NNFColdStart<T7744962>
"""
DECORATED_SECTION_NAME_PATTERN = re.compile(r'^(?P<section_name>.*?)(?:<0>)?(?:<(?P<command>.)(?P<argument>.*?)>)?$')
SYSTRACE_LINE_TYPES = set(['0', 'tracing_mark_write'])
class TraceLine(object):
def __init__(self, task, pid, tgid, cpu, flags, timestamp, function):
self.task = task
self.pid = pid
self.tgid = tgid
self.cpu = cpu
self.flags = flags
self.timestamp = timestamp
self.function = function
self.canceled = False
@property
def is_app_trace_line(self):
return isinstance(self.function, AppTraceFunction)
def cancel(self):
self.canceled = True
def __str__(self):
if self.canceled:
return ""
elif self.tgid:
return "{task:>16s}-{pid:<5d} ({tgid:5s}) [{cpu:03d}] {flags:4s} {timestamp:12f}: {function}\n".format(**vars(self))
elif self.flags:
return "{task:>16s}-{pid:<5d} [{cpu:03d}] {flags:4s} {timestamp:12f}: {function}\n".format(**vars(self))
else:
return "{task:>16s}-{pid:<5d} [{cpu:03d}] {timestamp:12.6f}: {function}\n".format(**vars(self))
class AppTraceFunction(object):
def __init__(self, type, args):
self.type = type
self.args = args
self.operation = args[0]
if len(args) >= 2 and args[1]:
self.pid = int(args[1])
if len(args) >= 3:
self._section_name, self.command, self.argument = _parse_section_name(args[2])
args[2] = self._section_name
else:
self._section_name = None
self.command = None
self.argument = None
self.cookie = None
@property
def section_name(self):
return self._section_name
@section_name.setter
def section_name(self, value):
self._section_name = value
self.args[2] = value
def __str__(self):
return "{type}: {args}".format(type=self.type, args='|'.join(self.args))
class AsyncTraceFunction(AppTraceFunction):
def __init__(self, type, args):
super(AsyncTraceFunction, self).__init__(type, args)
self.cookie = int(args[3])
TRACE_TYPE_MAP = {
'S': AsyncTraceFunction,
'T': AsyncTraceFunction,
'F': AsyncTraceFunction,
}
def parse_line(line):
match = TRACE_LINE_PATTERN.match(line.strip())
if not match:
return None
task = match.group("task")
pid = int(match.group("pid"))
tgid = match.group("tgid")
cpu = int(match.group("cpu"))
flags = match.group("flags")
timestamp = float(match.group("timestamp"))
function = match.group("function")
app_trace = _parse_function(function)
if app_trace:
function = app_trace
return TraceLine(task, pid, tgid, cpu, flags, timestamp, function)
def parse_dextr_line(line):
task = line["name"]
pid = line["pid"]
tgid = line["tid"]
cpu = None
flags = None
timestamp = line["ts"]
function = AppTraceFunction("DextrTrace", [line["ph"], line["pid"], line["name"]])
return TraceLine(task, pid, tgid, cpu, flags, timestamp, function)
def _parse_function(function):
line_match = APP_TRACE_LINE_PATTERN.match(function)
if not line_match:
return None
type = line_match.group("type")
if not type in SYSTRACE_LINE_TYPES:
return None
args = line_match.group("args").split('|')
if len(args) == 1 and len(args[0]) == 0:
args = None
constructor = TRACE_TYPE_MAP.get(args[0], AppTraceFunction)
return constructor(type, args)
def _parse_section_name(section_name):
if section_name is None:
return section_name, None, None
section_name_match = DECORATED_SECTION_NAME_PATTERN.match(section_name)
section_name = section_name_match.group("section_name")
command = section_name_match.group("command")
argument = section_name_match.group("argument")
return section_name, command, argument
def _format_section_name(section_name, command, argument):
if not command:
return section_name
return "{section_name}<{command}{argument}>".format(**vars())
class RoundTripFormattingTests(unittest.TestCase):
def testPlainSectionName(self):
section_name = "SectionName12345-5562342fas"
self.assertEqual(section_name, _format_section_name(*_parse_section_name(section_name)))
def testDecoratedSectionName(self):
section_name = "SectionName12345-5562342fas<D-123456>"
self.assertEqual(section_name, _format_section_name(*_parse_section_name(section_name)))
def testSimpleFunction(self):
function = "0: E"
self.assertEqual(function, str(_parse_function(function)))
def testFunctionWithoutCookie(self):
function = "0: B|27295|providerRemove"
self.assertEqual(function, str(_parse_function(function)))
def testFunctionWithCookie(self):
function = "0: S|27311|NNFColdStart|1112249168"
self.assertEqual(function, str(_parse_function(function)))
def testFunctionWithCookieAndArgs(self):
function = "0: T|27311|NNFColdStart|1122|Start"
self.assertEqual(function, str(_parse_function(function)))
def testFunctionWithArgsButNoPid(self):
function = "0: E|||foo=bar"
self.assertEqual(function, str(_parse_function(function)))
def testKitKatFunction(self):
function = "tracing_mark_write: B|14127|Looper.dispatchMessage|arg=>>>>> Dispatching to Handler (android.os.Handler) {422ae980} null: 0|Java"
self.assertEqual(function, str(_parse_function(function)))
def testNonSysTraceFunctionIgnored(self):
function = "sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=mmcqd/0 next_pid=120 next_prio=120"
self.assertEqual(None, _parse_function(function))
def testLineWithFlagsAndTGID(self):
line = " <idle>-0 ( 550) [000] d..2 7953.258473: cpu_idle: state=1 cpu_id=0\n"
self.assertEqual(line, str(parse_line(line)))
def testLineWithFlagsAndNoTGID(self):
line = " <idle>-0 (-----) [000] d..2 7953.258473: cpu_idle: state=1 cpu_id=0\n"
self.assertEqual(line, str(parse_line(line)))
def testLineWithFlags(self):
line = " <idle>-0 [001] ...2 3269.291072: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=mmcqd/0 next_pid=120 next_prio=120\n"
self.assertEqual(line, str(parse_line(line)))
def testLineWithoutFlags(self):
line = " <idle>-0 [001] 3269.291072: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=mmcqd/0 next_pid=120 next_prio=120\n"
self.assertEqual(line, str(parse_line(line)))
-7
View File
@@ -1,7 +0,0 @@
{
"rules": {
// This folder currently runs through babel and doesn't need to be
// compatible with node 4
"comma-dangle": 0
}
}
@@ -152,10 +152,7 @@
0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */,
0CF68AC21AF0540F00FF9E5C /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
0CF68AC21AF0540F00FF9E5C /* Products */ = {
isa = PBXGroup;
+5 -9
View File
@@ -15,15 +15,6 @@
@implementation ARTSurfaceView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.opaque = NO;
}
return self;
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
@@ -55,4 +46,9 @@
}
}
- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor
{
self.backgroundColor = inheritedBackgroundColor;
}
@end
+6 -6
View File
@@ -51,7 +51,7 @@
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
break;
default:
RCTLogError(@"Invalid CGPath type %llu at element %llu of %@", (unsigned long long)type, (unsigned long long)i, arr);
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
CGPathRelease(path);
return NULL;
}
@@ -158,7 +158,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
case 3: // pattern
return [[ARTPattern alloc] initWithArray:arr];
default:
RCTLogError(@"Unknown brush type: %llu", (unsigned long long)type);
RCTLogError(@"Unknown brush type: %zd", type);
return nil;
}
}
@@ -167,7 +167,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 2) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(2 + offset), arr);
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 2 + offset, arr);
return CGPointZero;
}
return (CGPoint){
@@ -180,7 +180,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr);
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
return CGRectZero;
}
return (CGRect){
@@ -193,7 +193,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr);
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
return NULL;
}
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
@@ -203,7 +203,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)offset, arr);
RCTLogError(@"Too few elements in array (expected at least %zd): %@", offset, arr);
return NULL;
}
arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
+19 -25
View File
@@ -101,29 +101,25 @@ var TextAttributes = merge(RenderableAttributes, {
// Native Components
var NativeSurfaceView = createReactNativeComponentClass('ARTSurfaceView',
() => ({
validAttributes: SurfaceViewAttributes,
uiViewClassName: 'ARTSurfaceView',
}));
var NativeSurfaceView = createReactNativeComponentClass({
validAttributes: SurfaceViewAttributes,
uiViewClassName: 'ARTSurfaceView',
});
var NativeGroup = createReactNativeComponentClass('ARTGroup',
() => ({
validAttributes: GroupAttributes,
uiViewClassName: 'ARTGroup',
}));
var NativeGroup = createReactNativeComponentClass({
validAttributes: GroupAttributes,
uiViewClassName: 'ARTGroup',
});
var NativeShape = createReactNativeComponentClass('ARTShape',
() => ({
validAttributes: ShapeAttributes,
uiViewClassName: 'ARTShape',
}));
var NativeShape = createReactNativeComponentClass({
validAttributes: ShapeAttributes,
uiViewClassName: 'ARTShape',
});
var NativeText = createReactNativeComponentClass('ARTText',
() => ({
validAttributes: TextAttributes,
uiViewClassName: 'ARTText',
}));
var NativeText = createReactNativeComponentClass({
validAttributes: TextAttributes,
uiViewClassName: 'ARTText',
});
// Utilities
@@ -396,7 +392,7 @@ 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();
var d = new Path(path).toJSON();
return (
<NativeShape
fill={extractBrush(props.fill, props)}
@@ -461,12 +457,11 @@ function extractFont(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,
fontWeight: font.fontWeight,
fontStyle: font.fontStyle,
};
}
@@ -490,8 +485,7 @@ function extractAlignment(alignment) {
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 textPath = props.path ? new Path(props.path).toJSON() : null;
var textFrame = extractFontAndLines(
props.font,
childrenAsString(props.children)
+23 -53
View File
@@ -8,58 +8,37 @@
*
* @providesModule ActionSheetIOS
* @flow
* @format
*/
'use strict';
const RCTActionSheetManager = require('NativeModules').ActionSheetManager;
var RCTActionSheetManager = require('NativeModules').ActionSheetManager;
const invariant = require('fbjs/lib/invariant');
const processColor = require('processColor');
var invariant = require('fbjs/lib/invariant');
var processColor = require('processColor');
/**
* Display action sheets and share sheets on iOS.
*
* See http://facebook.github.io/react-native/docs/actionsheetios.html
*/
const ActionSheetIOS = {
var ActionSheetIOS = {
/**
* Display an iOS action sheet.
*
* The `options` object must contain one or more of:
* Display an iOS action sheet. The `options` object must contain one or more
* of:
*
* - `options` (array of strings) - a list of button titles (required)
* - `cancelButtonIndex` (int) - index of cancel button in `options`
* - `destructiveButtonIndex` (int) - index of destructive button in `options`
* - `title` (string) - a title to show above the action sheet
* - `message` (string) - a message to show below the title
*
* The 'callback' function takes one parameter, the zero-based index
* of the selected item.
*
* See http://facebook.github.io/react-native/docs/actionsheetios.html#showactionsheetwithoptions
*/
showActionSheetWithOptions(
options: {|
+title?: ?string,
+message?: ?string,
+options: Array<string>,
+destructiveButtonIndex?: ?number,
+cancelButtonIndex?: ?number,
+anchor?: ?number,
+tintColor?: number | string,
|},
callback: (buttonIndex: number) => void,
) {
showActionSheetWithOptions(options: Object, callback: Function) {
invariant(
typeof options === 'object' && options !== null,
'Options must be a valid object',
'Options must be a valid object'
);
invariant(
typeof callback === 'function',
'Must provide a valid callback'
);
invariant(typeof callback === 'function', 'Must provide a valid callback');
RCTActionSheetManager.showActionSheetWithOptions(
{...options, tintColor: processColor(options.tintColor)},
callback,
callback
);
},
@@ -71,44 +50,35 @@ const ActionSheetIOS = {
* - `url` (string) - a URL to share
* - `message` (string) - a message to share
* - `subject` (string) - a subject for the message
* - `excludedActivityTypes` (array) - the activities to exclude from
* the ActionSheet
* - `tintColor` (color) - tint color of the buttons
* - `excludedActivityTypes` (array) - the activities to exclude from the ActionSheet
*
* The 'failureCallback' function takes one parameter, an error object.
* The only property defined on this object is an optional `stack` property
* of type `string`.
*
* The 'successCallback' function takes two parameters:
*
* - a boolean value signifying success or failure
* - a string that, in the case of success, indicates the method of sharing
*
* See http://facebook.github.io/react-native/docs/actionsheetios.html#showshareactionsheetwithoptions
* NOTE: if `url` points to a local file, or is a base64-encoded
* uri, the file it points to will be loaded and shared directly.
* In this way, you can share images, videos, PDF files, etc.
*/
showShareActionSheetWithOptions(
options: Object,
failureCallback: Function,
successCallback: Function,
successCallback: Function
) {
invariant(
typeof options === 'object' && options !== null,
'Options must be a valid object',
'Options must be a valid object'
);
invariant(
typeof failureCallback === 'function',
'Must provide a valid failureCallback',
'Must provide a valid failureCallback'
);
invariant(
typeof successCallback === 'function',
'Must provide a valid successCallback',
'Must provide a valid successCallback'
);
RCTActionSheetManager.showShareActionSheetWithOptions(
{...options, tintColor: processColor(options.tintColor)},
failureCallback,
successCallback,
successCallback
);
},
}
};
module.exports = ActionSheetIOS;
@@ -35,7 +35,6 @@
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
/* End PBXGroup section */
+38
View File
@@ -0,0 +1,38 @@
/**
* 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 AdSupportIOS
* @flow
*/
'use strict';
var AdSupport = require('NativeModules').AdSupport;
/**
* `AdSupport` provides access to the "advertising identifier". If you link this library
* in your project, you may need to justify your use for this identifier when submitting
* your application to the App Store.
*
* In order to use `AdSupport` in your project, you must link the `RCTAdSupport` library.
* In Xcode, you can manually add the `RCTAdSupport.m` and `RCTAdSupport.h` files from
* `node_modules/react-native/Libraries/AdSupport/` to the `Libraries/React/Base/` folder
* of your current project.
*
* You can refer to [Linking](docs/linking-libraries-ios.html) for help.
*
*/
module.exports = {
getAdvertisingId: function(onSuccess: Function, onFailure: Function) {
AdSupport.getAdvertisingId(onSuccess, onFailure);
},
getAdvertisingTrackingEnabled: function(onSuccess: Function, onFailure: Function) {
AdSupport.getAdvertisingTrackingEnabled(onSuccess, onFailure);
},
};
@@ -9,6 +9,6 @@
#import <React/RCTBridgeModule.h>
@interface RCTFileReaderModule : NSObject <RCTBridgeModule>
@interface RCTAdSupport : NSObject <RCTBridgeModule>
@end
+37
View File
@@ -0,0 +1,37 @@
/**
* 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 "RCTAdSupport.h"
#import <AdSupport/ASIdentifierManager.h>
#import <React/RCTUtils.h>
@implementation RCTAdSupport
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(getAdvertisingId:(RCTResponseSenderBlock)callback
withErrorCallback:(RCTResponseErrorBlock)errorCallback)
{
NSUUID *advertisingIdentifier = [ASIdentifierManager sharedManager].advertisingIdentifier;
if (advertisingIdentifier) {
callback(@[advertisingIdentifier.UUIDString]);
} else {
errorCallback(RCTErrorWithMessage(@"Advertising identifier is unavailable."));
}
}
RCT_EXPORT_METHOD(getAdvertisingTrackingEnabled:(RCTResponseSenderBlock)callback
withErrorCallback:(__unused RCTResponseSenderBlock)errorCallback)
{
callback(@[@([ASIdentifierManager sharedManager].advertisingTrackingEnabled)]);
}
@end
@@ -0,0 +1,257 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
832C819C1AAF6E1A007FA2F7 /* RCTAdSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 832C819B1AAF6E1A007FA2F7 /* RCTAdSupport.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
832C817E1AAF6DEF007FA2F7 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
832C81801AAF6DEF007FA2F7 /* libRCTAdSupport.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAdSupport.a; sourceTree = BUILT_PRODUCTS_DIR; };
832C819A1AAF6E1A007FA2F7 /* RCTAdSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTAdSupport.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
832C819B1AAF6E1A007FA2F7 /* RCTAdSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAdSupport.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
832C817D1AAF6DEF007FA2F7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
832C81771AAF6DEF007FA2F7 = {
isa = PBXGroup;
children = (
832C819A1AAF6E1A007FA2F7 /* RCTAdSupport.h */,
832C819B1AAF6E1A007FA2F7 /* RCTAdSupport.m */,
832C81811AAF6DEF007FA2F7 /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
832C81811AAF6DEF007FA2F7 /* Products */ = {
isa = PBXGroup;
children = (
832C81801AAF6DEF007FA2F7 /* libRCTAdSupport.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
832C817F1AAF6DEF007FA2F7 /* RCTAdSupport */ = {
isa = PBXNativeTarget;
buildConfigurationList = 832C81941AAF6DF0007FA2F7 /* Build configuration list for PBXNativeTarget "RCTAdSupport" */;
buildPhases = (
832C817C1AAF6DEF007FA2F7 /* Sources */,
832C817D1AAF6DEF007FA2F7 /* Frameworks */,
832C817E1AAF6DEF007FA2F7 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RCTAdSupport;
productName = RCTAdSupport;
productReference = 832C81801AAF6DEF007FA2F7 /* libRCTAdSupport.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
832C81781AAF6DEF007FA2F7 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0620;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
832C817F1AAF6DEF007FA2F7 = {
CreatedOnToolsVersion = 6.2;
};
};
};
buildConfigurationList = 832C817B1AAF6DEF007FA2F7 /* Build configuration list for PBXProject "RCTAdSupport" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 832C81771AAF6DEF007FA2F7;
productRefGroup = 832C81811AAF6DEF007FA2F7 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
832C817F1AAF6DEF007FA2F7 /* RCTAdSupport */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
832C817C1AAF6DEF007FA2F7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
832C819C1AAF6E1A007FA2F7 /* RCTAdSupport.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
832C81921AAF6DF0007FA2F7 /* 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_IMPLICIT_RETAIN_SELF = 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_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
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;
WARNING_CFLAGS = (
"-Werror",
"-Wall",
);
};
name = Debug;
};
832C81931AAF6DF0007FA2F7 /* 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_IMPLICIT_RETAIN_SELF = 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_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
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;
WARNING_CFLAGS = (
"-Werror",
"-Wall",
);
};
name = Release;
};
832C81951AAF6DF0007FA2F7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_STATIC_ANALYZER_MODE = deep;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
RUN_CLANG_STATIC_ANALYZER = YES;
};
name = Debug;
};
832C81961AAF6DF0007FA2F7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_STATIC_ANALYZER_MODE = deep;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
832C817B1AAF6DEF007FA2F7 /* Build configuration list for PBXProject "RCTAdSupport" */ = {
isa = XCConfigurationList;
buildConfigurations = (
832C81921AAF6DF0007FA2F7 /* Debug */,
832C81931AAF6DF0007FA2F7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
832C81941AAF6DF0007FA2F7 /* Build configuration list for PBXNativeTarget "RCTAdSupport" */ = {
isa = XCConfigurationList;
buildConfigurations = (
832C81951AAF6DF0007FA2F7 /* Debug */,
832C81961AAF6DF0007FA2F7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 832C81781AAF6DEF007FA2F7 /* Project object */;
}
+46 -8
View File
@@ -17,7 +17,7 @@ const Platform = require('Platform');
import type { AlertType, AlertButtonStyle } from 'AlertIOS';
export type Buttons = Array<{
type Buttons = Array<{
text?: string,
onPress?: ?Function,
style?: AlertButtonStyle,
@@ -30,16 +30,54 @@ type Options = {
/**
* Launches an alert dialog with the specified title and message.
*
* See http://facebook.github.io/react-native/docs/alert.html
*
* Optionally provide a list of buttons. Tapping any button will fire the
* respective onPress callback and dismiss the alert. By default, the only
* button will be an 'OK' button.
*
* This is an API that works both on iOS and Android and can show static
* alerts. To show an alert that prompts the user to enter some information,
* see `AlertIOS`; entering text in an alert is common on iOS only.
*
* ## iOS
*
* On iOS you can specify any number of buttons. Each button can optionally
* specify a style, which is one of 'default', 'cancel' or 'destructive'.
*
* ## Android
*
* On Android at most three buttons can be specified. Android has a concept
* of a neutral, negative and a positive button:
*
* - If you specify one button, it will be the 'positive' one (such as 'OK')
* - Two buttons mean 'negative', 'positive' (such as 'Cancel', 'OK')
* - Three buttons mean 'neutral', 'negative', 'positive' (such as 'Later', 'Cancel', 'OK')
*
* By default alerts on Android can be dismissed by tapping outside of the alert
* box. This event can be handled by providing an optional `options` parameter,
* with an `onDismiss` callback property `{ onDismiss: () => {} }`.
*
* Alternatively, the dismissing behavior can be disabled altogether by providing
* an optional `options` parameter with the `cancelable` property set to `false`
* i.e. `{ cancelable: false }`
*
* Example usage:
* ```
* // Works on both iOS and Android
* Alert.alert(
* 'Alert Title',
* 'My Alert Msg',
* [
* {text: 'Ask me later', onPress: () => console.log('Ask me later pressed')},
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
* {text: 'OK', onPress: () => console.log('OK Pressed')},
* ],
* { cancelable: false }
* )
* ```
*/
class Alert {
/**
* Launches an alert dialog with the specified title and message.
*
* See http://facebook.github.io/react-native/docs/alert.html#alert
*/
static alert(
title: ?string,
message?: ?string,
+92 -7
View File
@@ -77,15 +77,60 @@ export type ButtonsArray = Array<{
}>;
/**
* Use `AlertIOS` to display an alert dialog with a message or to create a prompt for user input on iOS. If you don't need to prompt for user input, we recommend using `Alert.alert() for cross-platform support.
* @description
* `AlertIOS` provides functionality to create an iOS alert dialog with a
* message or create a prompt for user input.
*
* Creating an iOS alert:
*
* ```
* AlertIOS.alert(
* 'Sync Complete',
* 'All your data are belong to us.'
* );
* ```
*
* Creating an iOS prompt:
*
* ```
* AlertIOS.prompt(
* 'Enter a value',
* null,
* text => console.log("You entered "+text)
* );
* ```
*
* We recommend using the [`Alert.alert`](docs/alert.html) method for
* cross-platform support if you don't need to create iOS-only prompts.
*
* See http://facebook.github.io/react-native/docs/alertios.html
*/
class AlertIOS {
/**
* Create and display a popup alert.
* @static
* @method alert
* @param title The dialog's title.
* @param message An optional message that appears below
* the dialog's title.
* @param callbackOrButtons This optional argument should
* be either a single-argument function or an array of buttons. If passed
* a function, it will be called when the user taps 'OK'.
*
* See http://facebook.github.io/react-native/docs/alertios.html#alert
* If passed an array of button configurations, each button should include
* a `text` key, as well as optional `onPress` and `style` keys. `style`
* should be one of 'default', 'cancel' or 'destructive'.
* @param type Deprecated, do not use.
*
* @example <caption>Example with custom buttons</caption>
*
* AlertIOS.alert(
* 'Update available',
* 'Keep your app up to date to enjoy the latest features',
* [
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
* {text: 'Install', onPress: () => console.log('Install Pressed')},
* ],
* );
*/
static alert(
title: ?string,
@@ -103,8 +148,48 @@ class AlertIOS {
/**
* Create and display a prompt to enter some text.
*
* See http://facebook.github.io/react-native/docs/alertios.html#prompt
* @static
* @method prompt
* @param title The dialog's title.
* @param message An optional message that appears above the text
* input.
* @param callbackOrButtons This optional argument should
* be either a single-argument function or an array of buttons. If passed
* a function, it will be called with the prompt's value when the user
* taps 'OK'.
*
* If passed an array of button configurations, each button should include
* a `text` key, as well as optional `onPress` and `style` keys (see
* example). `style` should be one of 'default', 'cancel' or 'destructive'.
* @param type This configures the text input. One of 'plain-text',
* 'secure-text' or 'login-password'.
* @param defaultValue The default text in text input.
* @param keyboardType The keyboard type of first text field(if exists).
* One of 'default', 'email-address', 'numeric', 'phone-pad',
* 'ascii-capable', 'numbers-and-punctuation', 'url', 'number-pad',
* 'name-phone-pad', 'decimal-pad', 'twitter' or 'web-search'.
*
* @example <caption>Example with custom buttons</caption>
*
* AlertIOS.prompt(
* 'Enter password',
* 'Enter your password to claim your $1.5B in lottery winnings',
* [
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
* {text: 'OK', onPress: password => console.log('OK Pressed, password: ' + password)},
* ],
* 'secure-text'
* );
*
* @example <caption>Example with the default button and a custom callback</caption>
*
* AlertIOS.prompt(
* 'Update username',
* null,
* text => console.log("Your username is "+text),
* null,
* 'default'
* );
*/
static prompt(
title: ?string,
@@ -124,7 +209,7 @@ class AlertIOS {
var callback = type;
var defaultValue = message;
RCTAlertManager.alertWithArgs({
title: title || '',
title: title || undefined,
type: 'plain-text',
defaultValue,
}, (id, value) => {
@@ -157,7 +242,7 @@ class AlertIOS {
}
RCTAlertManager.alertWithArgs({
title: title || '',
title: title || undefined,
message: message || undefined,
buttons,
type: type || undefined,
-196
View File
@@ -1,196 +0,0 @@
/**
* 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 AnimatedEvent
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('./nodes/AnimatedValue');
const NativeAnimatedHelper = require('./NativeAnimatedHelper');
const ReactNative = require('ReactNative');
const invariant = require('fbjs/lib/invariant');
const {shouldUseNativeDriver} = require('./NativeAnimatedHelper');
export type Mapping = {[key: string]: Mapping} | AnimatedValue;
export type EventConfig = {
listener?: ?Function,
useNativeDriver?: boolean,
};
function attachNativeEvent(
viewRef: any,
eventName: string,
argMapping: Array<?Mapping>,
) {
// Find animated values in `argMapping` and create an array representing their
// key path inside the `nativeEvent` object. Ex.: ['contentOffset', 'x'].
const eventMappings = [];
const traverse = (value, path) => {
if (value instanceof AnimatedValue) {
value.__makeNative();
eventMappings.push({
nativeEventPath: path,
animatedValueTag: value.__getNativeTag(),
});
} else if (typeof value === 'object') {
for (const key in value) {
traverse(value[key], path.concat(key));
}
}
};
invariant(
argMapping[0] && argMapping[0].nativeEvent,
'Native driven events only support animated values contained inside `nativeEvent`.',
);
// Assume that the event containing `nativeEvent` is always the first argument.
traverse(argMapping[0].nativeEvent, []);
const viewTag = ReactNative.findNodeHandle(viewRef);
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.addAnimatedEventToView(
viewTag,
eventName,
mapping,
);
});
return {
detach() {
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.removeAnimatedEventFromView(
viewTag,
eventName,
mapping.animatedValueTag,
);
});
},
};
}
class AnimatedEvent {
_argMapping: Array<?Mapping>;
_listeners: Array<Function> = [];
_callListeners: Function;
_attachedEvent: ?{
detach: () => void,
};
__isNative: boolean;
constructor(argMapping: Array<?Mapping>, config?: EventConfig = {}) {
this._argMapping = argMapping;
if (config.listener) {
this.__addListener(config.listener);
}
this._callListeners = this._callListeners.bind(this);
this._attachedEvent = null;
this.__isNative = shouldUseNativeDriver(config);
if (__DEV__) {
this._validateMapping();
}
}
__addListener(callback: Function): void {
this._listeners.push(callback);
}
__removeListener(callback: Function): void {
this._listeners = this._listeners.filter(listener => listener !== callback);
}
__attach(viewRef: any, eventName: string) {
invariant(
this.__isNative,
'Only native driven events need to be attached.',
);
this._attachedEvent = attachNativeEvent(
viewRef,
eventName,
this._argMapping,
);
}
__detach(viewTag: any, eventName: string) {
invariant(
this.__isNative,
'Only native driven events need to be detached.',
);
this._attachedEvent && this._attachedEvent.detach();
}
__getHandler() {
if (this.__isNative) {
return this._callListeners;
}
return (...args: any) => {
const traverse = (recMapping, recEvt, key) => {
if (typeof recEvt === 'number' && recMapping instanceof AnimatedValue) {
recMapping.setValue(recEvt);
} else if (typeof recMapping === 'object') {
for (const mappingKey in recMapping) {
/* $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. */
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
}
}
};
if (!this.__isNative) {
this._argMapping.forEach((mapping, idx) => {
traverse(mapping, args[idx], 'arg' + idx);
});
}
this._callListeners(...args);
};
}
_callListeners(...args) {
this._listeners.forEach(listener => listener(...args));
}
_validateMapping() {
const traverse = (recMapping, recEvt, key) => {
if (typeof recEvt === 'number') {
invariant(
recMapping instanceof AnimatedValue,
'Bad mapping of type ' +
typeof recMapping +
' for key ' +
key +
', event value must map to AnimatedValue',
);
return;
}
invariant(
typeof recMapping === 'object',
'Bad mapping of type ' + typeof recMapping + ' for key ' + key,
);
invariant(
typeof recEvt === 'object',
'Bad event of type ' + typeof recEvt + ' for key ' + key,
);
for (const mappingKey in recMapping) {
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
}
};
}
}
module.exports = {AnimatedEvent, attachNativeEvent};
File diff suppressed because it is too large Load Diff
+15 -10
View File
@@ -62,14 +62,14 @@ class Easing {
/**
* A stepping function, returns 1 for any positive value of `n`.
*/
static step0(n: number) {
static step0(n) {
return n > 0 ? 1 : 0;
}
/**
* A stepping function, returns 1 if `n` is greater than or equal to 1.
*/
static step1(n: number) {
static step1(n) {
return n >= 1 ? 1 : 0;
}
@@ -79,7 +79,7 @@ class Easing {
*
* http://cubic-bezier.com/#0,0,1,1
*/
static linear(t: number) {
static linear(t) {
return t;
}
@@ -102,7 +102,7 @@ class Easing {
*
* http://easings.net/#easeInQuad
*/
static quad(t: number) {
static quad(t) {
return t * t;
}
@@ -112,7 +112,7 @@ class Easing {
*
* http://easings.net/#easeInCubic
*/
static cubic(t: number) {
static cubic(t) {
return t * t * t;
}
@@ -122,8 +122,8 @@ class Easing {
* n = 4: http://easings.net/#easeInQuart
* n = 5: http://easings.net/#easeInQuint
*/
static poly(n: number) {
return (t: number) => Math.pow(t, n);
static poly(n) {
return (t) => Math.pow(t, n);
}
/**
@@ -131,7 +131,7 @@ class Easing {
*
* http://easings.net/#easeInSine
*/
static sin(t: number) {
static sin(t) {
return 1 - Math.cos(t * Math.PI / 2);
}
@@ -140,7 +140,7 @@ class Easing {
*
* http://easings.net/#easeInCirc
*/
static circle(t: number) {
static circle(t) {
return 1 - Math.sqrt(1 - t * t);
}
@@ -149,7 +149,7 @@ class Easing {
*
* http://easings.net/#easeInExpo
*/
static exp(t: number) {
static exp(t) {
return Math.pow(2, 10 * (t - 1));
}
@@ -162,6 +162,11 @@ class Easing {
* times.
*
* http://easings.net/#easeInElastic
*
* Wolfram Plots:
*
* - http://tiny.cc/elastic_b_1 (bounciness = 1, default)
* - http://tiny.cc/elastic_b_3 (bounciness = 3)
*/
static elastic(bounciness: number = 1): (t: number) => number {
const p = bounciness * Math.PI;
@@ -6,19 +6,14 @@
* 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 AnimatedInterpolation
* @providesModule Interpolation
* @flow
* @format
*/
/* eslint no-bitwise: 0 */
'use strict';
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
const invariant = require('fbjs/lib/invariant');
const normalizeColor = require('normalizeColor');
var invariant = require('fbjs/lib/invariant');
var normalizeColor = require('normalizeColor');
type ExtrapolateType = 'extend' | 'identity' | 'clamp';
@@ -28,76 +23,74 @@ export type InterpolationConfigType = {
* detected during the deployment of v0.38.0. To see the error, remove this
* comment and run flow
*/
outputRange: Array<number> | Array<string>,
easing?: (input: number) => number,
outputRange: (Array<number> | Array<string>),
easing?: ((input: number) => number),
extrapolate?: ExtrapolateType,
extrapolateLeft?: ExtrapolateType,
extrapolateRight?: ExtrapolateType,
};
const linear = t => t;
var linear = (t) => t;
/**
* Very handy helper to map input ranges to output ranges with an easing
* function and custom behavior outside of the ranges.
*/
function createInterpolation(
config: InterpolationConfigType,
): (input: number) => number | string {
if (config.outputRange && typeof config.outputRange[0] === 'string') {
return createInterpolationFromStringOutputRange(config);
}
class Interpolation {
static create(config: InterpolationConfigType): (input: number) => number | string {
const outputRange: Array<number> = (config.outputRange: any);
checkInfiniteRange('outputRange', outputRange);
if (config.outputRange && typeof config.outputRange[0] === 'string') {
return createInterpolationFromStringOutputRange(config);
}
const inputRange = config.inputRange;
checkInfiniteRange('inputRange', inputRange);
checkValidInputRange(inputRange);
var outputRange: Array<number> = (config.outputRange: any);
checkInfiniteRange('outputRange', outputRange);
invariant(
inputRange.length === outputRange.length,
'inputRange (' +
inputRange.length +
') and outputRange (' +
outputRange.length +
') must have the same length',
);
var inputRange = config.inputRange;
checkInfiniteRange('inputRange', inputRange);
checkValidInputRange(inputRange);
const easing = config.easing || linear;
let extrapolateLeft: ExtrapolateType = 'extend';
if (config.extrapolateLeft !== undefined) {
extrapolateLeft = config.extrapolateLeft;
} else if (config.extrapolate !== undefined) {
extrapolateLeft = config.extrapolate;
}
let extrapolateRight: ExtrapolateType = 'extend';
if (config.extrapolateRight !== undefined) {
extrapolateRight = config.extrapolateRight;
} else if (config.extrapolate !== undefined) {
extrapolateRight = config.extrapolate;
}
return input => {
invariant(
typeof input === 'number',
'Cannot interpolation an input which is not a number',
inputRange.length === outputRange.length,
'inputRange (' + inputRange.length + ') and outputRange (' +
outputRange.length + ') must have the same length'
);
const range = findRange(input, inputRange);
return interpolate(
input,
inputRange[range],
inputRange[range + 1],
outputRange[range],
outputRange[range + 1],
easing,
extrapolateLeft,
extrapolateRight,
);
};
var easing = config.easing || linear;
var extrapolateLeft: ExtrapolateType = 'extend';
if (config.extrapolateLeft !== undefined) {
extrapolateLeft = config.extrapolateLeft;
} else if (config.extrapolate !== undefined) {
extrapolateLeft = config.extrapolate;
}
var extrapolateRight: ExtrapolateType = 'extend';
if (config.extrapolateRight !== undefined) {
extrapolateRight = config.extrapolateRight;
} else if (config.extrapolate !== undefined) {
extrapolateRight = config.extrapolate;
}
return (input) => {
invariant(
typeof input === 'number',
'Cannot interpolation an input which is not a number'
);
var range = findRange(input, inputRange);
return interpolate(
input,
inputRange[range],
inputRange[range + 1],
outputRange[range],
outputRange[range + 1],
easing,
extrapolateLeft,
extrapolateRight,
);
};
}
}
function interpolate(
@@ -106,11 +99,11 @@ function interpolate(
inputMax: number,
outputMin: number,
outputMax: number,
easing: (input: number) => number,
easing: ((input: number) => number),
extrapolateLeft: ExtrapolateType,
extrapolateRight: ExtrapolateType,
) {
let result = input;
var result = input;
// Extrapolate
if (result < inputMin) {
@@ -169,22 +162,22 @@ function interpolate(
}
function colorToRgba(input: string): string {
let int32Color = normalizeColor(input);
var int32Color = normalizeColor(input);
if (int32Color === null) {
return input;
}
int32Color = int32Color || 0;
const r = (int32Color & 0xff000000) >>> 24;
const g = (int32Color & 0x00ff0000) >>> 16;
const b = (int32Color & 0x0000ff00) >>> 8;
const a = (int32Color & 0x000000ff) / 255;
var r = (int32Color & 0xff000000) >>> 24;
var g = (int32Color & 0x00ff0000) >>> 16;
var b = (int32Color & 0x0000ff00) >>> 8;
var a = (int32Color & 0x000000ff) / 255;
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
const stringShapeRegex = /[0-9\.-]+/g;
var stringShapeRegex = /[0-9\.-]+/g;
/**
* Supports string shapes by extracting numbers so new values can be computed,
@@ -197,7 +190,7 @@ const stringShapeRegex = /[0-9\.-]+/g;
function createInterpolationFromStringOutputRange(
config: InterpolationConfigType,
): (input: number) => string {
let outputRange: Array<string> = (config.outputRange: any);
var outputRange: Array<string> = (config.outputRange: any);
invariant(outputRange.length >= 2, 'Bad output range');
outputRange = outputRange.map(colorToRgba);
checkPattern(outputRange);
@@ -213,7 +206,7 @@ function createInterpolationFromStringOutputRange(
/* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to
* guard against this possibility.
*/
const outputRanges = outputRange[0].match(stringShapeRegex).map(() => []);
var outputRanges = outputRange[0].match(stringShapeRegex).map(() => []);
outputRange.forEach(value => {
/* $FlowFixMe(>=0.18.0): `value.match()` can return `null`. Need to guard
* against this possibility.
@@ -226,28 +219,25 @@ function createInterpolationFromStringOutputRange(
/* $FlowFixMe(>=0.18.0): `outputRange[0].match()` can return `null`. Need to
* guard against this possibility.
*/
const interpolations = outputRange[0]
.match(stringShapeRegex)
.map((value, i) => {
return createInterpolation({
...config,
outputRange: outputRanges[i],
});
var interpolations = outputRange[0].match(stringShapeRegex).map((value, i) => {
return Interpolation.create({
...config,
outputRange: outputRanges[i],
});
});
// rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to
// round the opacity (4th column).
const shouldRound = isRgbOrRgba(outputRange[0]);
return input => {
let i = 0;
return (input) => {
var i = 0;
// 'rgba(0, 100, 200, 0)'
// ->
// 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
return outputRange[0].replace(stringShapeRegex, () => {
const val = +interpolations[i++](input);
const rounded =
shouldRound && i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000;
const rounded = shouldRound && i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000;
return String(rounded);
});
};
@@ -258,8 +248,8 @@ function isRgbOrRgba(range) {
}
function checkPattern(arr: Array<string>) {
const pattern = arr[0].replace(stringShapeRegex, '');
for (let i = 1; i < arr.length; ++i) {
var pattern = arr[0].replace(stringShapeRegex, '');
for (var i = 1; i < arr.length; ++i) {
invariant(
pattern === arr[i].replace(stringShapeRegex, ''),
'invalid pattern ' + arr[0] + ' and ' + arr[i],
@@ -268,8 +258,7 @@ function checkPattern(arr: Array<string>) {
}
function findRange(input: number, inputRange: Array<number>) {
let i;
for (i = 1; i < inputRange.length - 1; ++i) {
for (var i = 1; i < inputRange.length - 1; ++i) {
if (inputRange[i] >= input) {
break;
}
@@ -279,7 +268,7 @@ function findRange(input: number, inputRange: Array<number>) {
function checkValidInputRange(arr: Array<number>) {
invariant(arr.length >= 2, 'inputRange must have at least 2 elements');
for (let i = 1; i < arr.length; ++i) {
for (var i = 1; i < arr.length; ++i) {
invariant(
arr[i] >= arr[i - 1],
/* $FlowFixMe(>=0.13.0) - In the addition expression below this comment,
@@ -288,7 +277,7 @@ function checkValidInputRange(arr: Array<number>) {
* mean this implicit string conversion, you can do something like
* String(myThing)
*/
'inputRange must be monotonically increasing ' + arr,
'inputRange must be monotonically increasing ' + arr
);
}
}
@@ -303,86 +292,8 @@ function checkInfiniteRange(name: string, arr: Array<number>) {
* this implicit string conversion, you can do something like
* String(myThing)
*/
name + 'cannot be ]-infinity;+infinity[ ' + arr,
name + 'cannot be ]-infinity;+infinity[ ' + arr
);
}
class AnimatedInterpolation extends AnimatedWithChildren {
// Export for testing.
static __createInterpolation = createInterpolation;
_parent: AnimatedNode;
_config: InterpolationConfigType;
_interpolation: (input: number) => number | string;
constructor(parent: AnimatedNode, config: InterpolationConfigType) {
super();
this._parent = parent;
this._config = config;
this._interpolation = createInterpolation(config);
}
__makeNative() {
this._parent.__makeNative();
super.__makeNative();
}
__getValue(): number | string {
const parentValue: number = this._parent.__getValue();
invariant(
typeof parentValue === 'number',
'Cannot interpolate an input which is not a number.',
);
return this._interpolation(parentValue);
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._parent.__addChild(this);
}
__detach(): void {
this._parent.__removeChild(this);
super.__detach();
}
__transformDataType(range: Array<any>) {
// Change the string array type to number array
// So we can reuse the same logic in iOS and Android platform
return range.map(function(value) {
if (typeof value !== 'string') {
return value;
}
if (/deg$/.test(value)) {
const degrees = parseFloat(value) || 0;
const radians = degrees * Math.PI / 180.0;
return radians;
} else {
// Assume radians
return parseFloat(value) || 0;
}
});
}
__getNativeConfig(): any {
if (__DEV__) {
NativeAnimatedHelper.validateInterpolation(this._config);
}
return {
inputRange: this._config.inputRange,
// Only the `outputRange` can contain strings so we don't need to transform `inputRange` here
outputRange: this.__transformDataType(this._config.outputRange),
extrapolateLeft:
this._config.extrapolateLeft || this._config.extrapolate || 'extend',
extrapolateRight:
this._config.extrapolateRight || this._config.extrapolate || 'extend',
type: 'interpolation',
};
}
}
module.exports = AnimatedInterpolation;
module.exports = Interpolation;
+24 -106
View File
@@ -8,7 +8,6 @@
*
* @providesModule NativeAnimatedHelper
* @flow
* @format
*/
'use strict';
@@ -17,9 +16,6 @@ const NativeEventEmitter = require('NativeEventEmitter');
const invariant = require('fbjs/lib/invariant');
import type {AnimationConfig} from './animations/Animation';
import type {EventConfig} from './AnimatedEvent';
let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
let __nativeAnimationIdCount = 1; /* used for started animations */
@@ -53,26 +49,13 @@ const API = {
assertNativeAnimatedModule();
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag);
},
disconnectAnimatedNodes: function(
parentTag: ?number,
childTag: ?number,
): void {
disconnectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag);
},
startAnimatingNode: function(
animationId: ?number,
nodeTag: ?number,
config: Object,
endCallback: EndCallback,
): void {
startAnimatingNode: function(animationId: ?number, nodeTag: ?number, config: Object, endCallback: EndCallback): void {
assertNativeAnimatedModule();
NativeAnimatedModule.startAnimatingNode(
animationId,
nodeTag,
config,
endCallback,
);
NativeAnimatedModule.startAnimatingNode(animationId, nodeTag, config, endCallback);
},
stopAnimation: function(animationId: ?number) {
assertNativeAnimatedModule();
@@ -94,17 +77,11 @@ const API = {
assertNativeAnimatedModule();
NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag);
},
connectAnimatedNodeToView: function(
nodeTag: ?number,
viewTag: ?number,
): void {
connectAnimatedNodeToView: function(nodeTag: ?number, viewTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag);
},
disconnectAnimatedNodeFromView: function(
nodeTag: ?number,
viewTag: ?number,
): void {
disconnectAnimatedNodeFromView: function(nodeTag: ?number, viewTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag);
},
@@ -112,30 +89,14 @@ const API = {
assertNativeAnimatedModule();
NativeAnimatedModule.dropAnimatedNode(tag);
},
addAnimatedEventToView: function(
viewTag: ?number,
eventName: string,
eventMapping: EventMapping,
) {
addAnimatedEventToView: function(viewTag: ?number, eventName: string, eventMapping: EventMapping) {
assertNativeAnimatedModule();
NativeAnimatedModule.addAnimatedEventToView(
viewTag,
eventName,
eventMapping,
);
NativeAnimatedModule.addAnimatedEventToView(viewTag, eventName, eventMapping);
},
removeAnimatedEventFromView(
viewTag: ?number,
eventName: string,
animatedNodeTag: ?number,
) {
removeAnimatedEventFromView(viewTag: ?number, eventName: string, animatedNodeTag: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.removeAnimatedEventFromView(
viewTag,
eventName,
animatedNodeTag,
);
},
NativeAnimatedModule.removeAnimatedEventFromView(viewTag, eventName, animatedNodeTag);
}
};
/**
@@ -147,9 +108,6 @@ const API = {
const STYLES_WHITELIST = {
opacity: true,
transform: true,
/* ios styles */
shadowOpacity: true,
shadowRadius: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
@@ -169,34 +127,10 @@ const TRANSFORM_WHITELIST = {
perspective: true,
};
const SUPPORTED_INTERPOLATION_PARAMS = {
inputRange: true,
outputRange: true,
extrapolate: true,
extrapolateRight: true,
extrapolateLeft: true,
};
function addWhitelistedStyleProp(prop: string): void {
STYLES_WHITELIST[prop] = true;
}
function addWhitelistedTransformProp(prop: string): void {
TRANSFORM_WHITELIST[prop] = true;
}
function addWhitelistedInterpolationParam(param: string): void {
SUPPORTED_INTERPOLATION_PARAMS[param] = true;
}
function validateTransform(configs: Array<Object>): void {
configs.forEach(config => {
configs.forEach((config) => {
if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) {
throw new Error(
`Property '${
config.property
}' is not supported by native animated module`,
);
throw new Error(`Property '${config.property}' is not supported by native animated module`);
}
});
}
@@ -204,19 +138,22 @@ function validateTransform(configs: Array<Object>): void {
function validateStyles(styles: Object): void {
for (var key in styles) {
if (!STYLES_WHITELIST.hasOwnProperty(key)) {
throw new Error(
`Style property '${key}' is not supported by native animated module`,
);
throw new Error(`Style property '${key}' is not supported by native animated module`);
}
}
}
function validateInterpolation(config: Object): void {
var SUPPORTED_INTERPOLATION_PARAMS = {
inputRange: true,
outputRange: true,
extrapolate: true,
extrapolateRight: true,
extrapolateLeft: true,
};
for (var key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
throw new Error(
`Interpolation property '${key}' is not supported by native animated module`,
);
throw new Error(`Interpolation property '${key}' is not supported by native animated module`);
}
}
}
@@ -233,38 +170,19 @@ function assertNativeAnimatedModule(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
}
let _warnedMissingNativeAnimated = false;
function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean {
if (config.useNativeDriver && !NativeAnimatedModule) {
if (!_warnedMissingNativeAnimated) {
console.warn(
'Animated: `useNativeDriver` is not supported because the native ' +
'animated module is missing. Falling back to JS-based animation. To ' +
'resolve this, add `RCTAnimation` module to this app, or remove ' +
'`useNativeDriver`. ' +
'More info: https://github.com/facebook/react-native/issues/11094#issuecomment-263240420',
);
_warnedMissingNativeAnimated = true;
}
return false;
}
return config.useNativeDriver || false;
function isNativeAnimatedAvailable(): boolean {
return !!NativeAnimatedModule;
}
module.exports = {
API,
addWhitelistedStyleProp,
addWhitelistedTransformProp,
addWhitelistedInterpolationParam,
validateStyles,
validateTransform,
validateInterpolation,
generateNewNodeTag,
generateNewAnimationId,
assertNativeAnimatedModule,
shouldUseNativeDriver,
isNativeAnimatedAvailable,
get nativeEventEmitter() {
if (!nativeEventEmitter) {
nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);
+8 -8
View File
@@ -13,15 +13,15 @@
'use strict';
type SpringConfigType = {
stiffness: number,
damping: number,
tension: number,
friction: number,
};
function stiffnessFromOrigamiValue(oValue) {
function tensionFromOrigamiValue(oValue) {
return (oValue - 30) * 3.62 + 194;
}
function dampingFromOrigamiValue(oValue) {
function frictionFromOrigamiValue(oValue) {
return (oValue - 8) * 3 + 25;
}
@@ -30,8 +30,8 @@ function fromOrigamiTensionAndFriction(
friction: number,
): SpringConfigType {
return {
stiffness: stiffnessFromOrigamiValue(tension),
damping: dampingFromOrigamiValue(friction),
tension: tensionFromOrigamiValue(tension),
friction: frictionFromOrigamiValue(friction)
};
}
@@ -91,8 +91,8 @@ function fromBouncinessAndSpeed(
);
return {
stiffness: stiffnessFromOrigamiValue(bouncyTension),
damping: dampingFromOrigamiValue(bouncyFriction),
tension: tensionFromOrigamiValue(bouncyTension),
friction: frictionFromOrigamiValue(bouncyFriction)
};
}
@@ -5,11 +5,11 @@
* 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.
*
* @emails oncall+react_native
*/
'use strict';
jest.disableAutomock();
var Animated = require('Animated');
describe('Animated tests', () => {
beforeEach(() => {
@@ -94,11 +94,11 @@ describe('Animated tests', () => {
opacity: anim,
},
};
c.UNSAFE_componentWillMount();
c.componentWillMount();
expect(anim.__detach).not.toBeCalled();
c._component = {};
c.UNSAFE_componentWillReceiveProps({
c.componentWillReceiveProps({
style: {
opacity: anim,
},
@@ -120,7 +120,7 @@ describe('Animated tests', () => {
opacity: anim,
},
};
c.UNSAFE_componentWillMount();
c.componentWillMount();
Animated.timing(anim, {toValue: 10, duration: 1000}).start(callback);
c._component = {};
@@ -137,7 +137,7 @@ describe('Animated tests', () => {
expect(callback).toBeCalled();
});
it('send toValue when an underdamped spring stops', () => {
it('send toValue when a spring stops', () => {
var anim = new Animated.Value(0);
var listener = jest.fn();
anim.addListener(listener);
@@ -149,18 +149,6 @@ describe('Animated tests', () => {
expect(anim.__getValue()).toBe(15);
});
it('send toValue when a critically damped spring stops', () => {
var anim = new Animated.Value(0);
var listener = jest.fn();
anim.addListener(listener);
Animated.spring(anim, {stiffness: 8000, damping: 2000, toValue: 15}).start();
jest.runAllTimers();
var lastValue = listener.mock.calls[listener.mock.calls.length - 2][0].value;
expect(lastValue).not.toBe(15);
expect(lastValue).toBeCloseTo(15);
expect(anim.__getValue()).toBe(15);
});
it('convert to JSON', () => {
expect(JSON.stringify(new Animated.Value(10))).toBe('10');
});
@@ -371,7 +359,7 @@ describe('Animated tests', () => {
loop.start(cb);
expect(animation.start).not.toBeCalled();
expect(cb).toBeCalledWith({finished: true});
expect(cb).toBeCalledWith({ finished: true });
});
it('supports interrupting an indefinite loop', () => {
@@ -5,13 +5,11 @@
* 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.
*
* @emails oncall+react_native
*/
'use strict';
jest
.clearAllMocks()
.disableAutomock()
.setMock('Text', {})
.setMock('View', {})
.setMock('Image', {})
@@ -29,7 +27,7 @@ const NativeAnimatedHelper = require('NativeAnimatedHelper');
function createAndMountComponent(ComponentClass, props) {
const component = new ComponentClass();
component.props = props;
component.UNSAFE_componentWillMount();
component.componentWillMount();
// Simulate that refs were set.
component._component = {};
component.componentDidMount();
@@ -80,7 +78,7 @@ describe('Native Animated', () => {
anim.setValue(0.5);
expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(expect.any(Number), 0.5);
expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(jasmine.any(Number), 0.5);
expect(c.refs.node.setNativeProps).not.toHaveBeenCalled();
});
@@ -95,12 +93,12 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
{type: 'value', value: 0, offset: 10},
);
anim.setOffset(20);
expect(nativeAnimatedModule.setAnimatedNodeOffset)
.toBeCalledWith(expect.any(Number), 20);
.toBeCalledWith(jasmine.any(Number), 20);
});
it('should flatten offset', () => {
@@ -113,12 +111,12 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
{type: 'value', value: 0, offset: 0},
);
anim.flattenOffset();
expect(nativeAnimatedModule.flattenAnimatedNodeOffset)
.toBeCalledWith(expect.any(Number));
.toBeCalledWith(jasmine.any(Number));
});
it('should extract offset', () => {
@@ -131,12 +129,12 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
{type: 'value', value: 0, offset: 0},
);
anim.extractOffset();
expect(nativeAnimatedModule.extractAnimatedNodeOffset)
.toBeCalledWith(expect.any(Number));
.toBeCalledWith(jasmine.any(Number));
});
});
@@ -214,14 +212,14 @@ describe('Native Animated', () => {
);
const c = createAndMountComponent(Animated.View, {onTouchMove: event});
expect(nativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
'onTouchMove',
{nativeEventPath: ['state', 'foo'], animatedValueTag: value.__getNativeTag()},
);
c.componentWillUnmount();
expect(nativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
'onTouchMove',
value.__getNativeTag(),
);
@@ -271,10 +269,10 @@ describe('Native Animated', () => {
expect(nativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(2);
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1},
expect.any(Function)
jasmine.any(Number),
jasmine.any(Number),
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1},
jasmine.any(Function)
);
expect(nativeAnimatedModule.disconnectAnimatedNodes).toHaveBeenCalledTimes(2);
@@ -292,11 +290,11 @@ describe('Native Animated', () => {
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), {type: 'value', value: 0, offset: 0});
.toBeCalledWith(jasmine.any(Number), {type: 'value', value: 0, offset: 0});
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), {type: 'style', style: {opacity: expect.any(Number)}});
.toBeCalledWith(jasmine.any(Number), {type: 'style', style: {opacity: jasmine.any(Number)}});
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), {type: 'props', props: {style: expect.any(Number)}});
.toBeCalledWith(jasmine.any(Number), {type: 'props', props: {style: jasmine.any(Number)}});
});
it('sends a valid graph description for Animated.add nodes', () => {
@@ -312,8 +310,8 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'addition', input: expect.any(Array)},
jasmine.any(Number),
{type: 'addition', input: jasmine.any(Array)},
);
const additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'addition'
@@ -344,8 +342,8 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'multiplication', input: expect.any(Array)},
jasmine.any(Number),
{type: 'multiplication', input: jasmine.any(Array)},
);
const multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'multiplication'
@@ -376,7 +374,7 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), {type: 'division', input: expect.any(Array)});
.toBeCalledWith(jasmine.any(Number), {type: 'division', input: jasmine.any(Array)});
const divisionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'division'
);
@@ -404,8 +402,8 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'modulus', modulus: 4, input: expect.any(Number)},
jasmine.any(Number),
{type: 'modulus', modulus: 4, input: jasmine.any(Number)},
);
const moduloCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'modulus'
@@ -435,11 +433,11 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
{type: 'value', value: 10, offset: 0}
);
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), {
.toBeCalledWith(jasmine.any(Number), {
type: 'interpolation',
inputRange: [10, 20],
outputRange: [0, 1],
@@ -466,11 +464,11 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
jasmine.any(Number),
{
type: 'transform',
transforms: [{
nodeTag: expect.any(Number),
nodeTag: jasmine.any(Number),
property: 'translateX',
type: 'animated',
}, {
@@ -493,8 +491,8 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'diffclamp', input: expect.any(Number), max: 20, min: 0},
jasmine.any(Number),
{type: 'diffclamp', input: jasmine.any(Number), max: 20, min: 0},
);
const diffClampCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'diffclamp'
@@ -570,9 +568,9 @@ describe('Native Animated', () => {
});
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), { type: 'style', style: { opacity: expect.any(Number) }});
.toBeCalledWith(jasmine.any(Number), { type: 'style', style: { opacity: jasmine.any(Number) }});
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), { type: 'props', props: { style: expect.any(Number) }});
.toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }});
});
});
@@ -582,10 +580,10 @@ describe('Native Animated', () => {
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1},
expect.any(Function)
jasmine.any(Number),
jasmine.any(Number),
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1},
jasmine.any(Function)
);
});
@@ -593,65 +591,38 @@ describe('Native Animated', () => {
const anim = new Animated.Value(0);
Animated.spring(anim, {toValue: 10, friction: 5, tension: 164, useNativeDriver: true}).start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
jasmine.any(Number),
jasmine.any(Number),
{
type: 'spring',
stiffness: 679.08,
damping: 16,
mass: 1,
friction: 16,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
tension: 679.08,
toValue: 10,
iterations: 1,
},
expect.any(Function)
);
Animated.spring(anim, {
toValue: 10,
stiffness: 1000,
damping: 500,
mass: 3,
useNativeDriver: true
}).start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
stiffness: 1000,
damping: 500,
mass: 3,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
toValue: 10,
iterations: 1,
},
expect.any(Function)
jasmine.any(Function)
);
Animated.spring(anim, {toValue: 10, bounciness: 8, speed: 10, useNativeDriver: true}).start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
jasmine.any(Number),
jasmine.any(Number),
{
type: 'spring',
damping: 23.05223140901191,
friction: 23.05223140901191,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
stiffness: 299.61882352941177,
mass: 1,
tension: 299.61882352941177,
toValue: 10,
iterations: 1,
},
expect.any(Function)
jasmine.any(Function)
);
});
@@ -660,10 +631,10 @@ describe('Native Animated', () => {
Animated.decay(anim, {velocity: 10, deceleration: 0.1, useNativeDriver: true}).start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
jasmine.any(Number),
jasmine.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1},
expect.any(Function)
jasmine.any(Function)
);
});
@@ -675,10 +646,10 @@ describe('Native Animated', () => {
).start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
jasmine.any(Number),
jasmine.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10},
expect.any(Function)
jasmine.any(Function)
);
});
@@ -688,10 +659,10 @@ describe('Native Animated', () => {
animation.start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1},
expect.any(Function)
jasmine.any(Number),
jasmine.any(Number),
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1},
jasmine.any(Function)
);
const animationId = nativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
@@ -5,11 +5,11 @@
* 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.
*
* @emails oncall+react_native
*/
'use strict';
jest.unmock('Easing');
var Easing = require('Easing');
describe('Easing', () => {
it('should work with linear', () => {
@@ -5,17 +5,20 @@
* 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.
*
* @emails oncall+react_native
*/
'use strict';
var AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
jest
.unmock('Interpolation')
.unmock('Easing')
.unmock('normalizeColor');
var Interpolation = require('Interpolation');
var Easing = require('Easing');
describe('Interpolation', () => {
it('should work with defaults', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [0, 1],
});
@@ -27,7 +30,7 @@ describe('Interpolation', () => {
});
it('should work with output range', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [100, 200],
});
@@ -39,7 +42,7 @@ describe('Interpolation', () => {
});
it('should work with input range', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [100, 200],
outputRange: [0, 1],
});
@@ -51,23 +54,19 @@ describe('Interpolation', () => {
});
it('should throw for non monotonic input ranges', () => {
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [0, 2, 1],
outputRange: [0, 1, 2],
}),
).toThrow();
expect(() => Interpolation.create({
inputRange: [0, 2, 1],
outputRange: [0, 1, 2],
})).toThrow();
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1, 2],
outputRange: [0, 3, 1],
}),
).not.toThrow();
expect(() => Interpolation.create({
inputRange: [0, 1, 2],
outputRange: [0, 3, 1],
})).not.toThrow();
});
it('should work with empty input range', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 10, 10],
outputRange: [1, 2, 3],
extrapolate: 'extend',
@@ -81,7 +80,7 @@ describe('Interpolation', () => {
});
it('should work with empty output range', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [1, 2, 3],
outputRange: [0, 10, 10],
extrapolate: 'extend',
@@ -96,7 +95,7 @@ describe('Interpolation', () => {
});
it('should work with easing', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [0, 1],
easing: Easing.quad,
@@ -109,7 +108,7 @@ describe('Interpolation', () => {
});
it('should work with extrapolate', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'extend',
@@ -119,7 +118,7 @@ describe('Interpolation', () => {
expect(interpolation(-2)).toBe(4);
expect(interpolation(2)).toBe(4);
interpolation = AnimatedInterpolation.__createInterpolation({
interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
@@ -129,7 +128,7 @@ describe('Interpolation', () => {
expect(interpolation(-2)).toBe(0);
expect(interpolation(2)).toBe(1);
interpolation = AnimatedInterpolation.__createInterpolation({
interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'identity',
@@ -141,7 +140,7 @@ describe('Interpolation', () => {
});
it('should work with keyframes with extrapolate', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 10, 100, 1000],
outputRange: [0, 5, 50, 500],
extrapolate: true,
@@ -159,7 +158,7 @@ describe('Interpolation', () => {
});
it('should work with keyframes without extrapolate', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1, 2],
outputRange: [0.2, 1, 0.2],
extrapolate: 'clamp',
@@ -169,23 +168,19 @@ describe('Interpolation', () => {
});
it('should throw for an infinite input range', () => {
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [-Infinity, Infinity],
outputRange: [0, 1],
}),
).toThrow();
expect(() => Interpolation.create({
inputRange: [-Infinity, Infinity],
outputRange: [0, 1],
})).toThrow();
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [-Infinity, 0, Infinity],
outputRange: [1, 2, 3],
}),
).not.toThrow();
expect(() => Interpolation.create({
inputRange: [-Infinity, 0, Infinity],
outputRange: [1, 2, 3],
})).not.toThrow();
});
it('should work with negative infinite', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [-Infinity, 0],
outputRange: [-Infinity, 0],
easing: Easing.quad,
@@ -201,7 +196,7 @@ describe('Interpolation', () => {
});
it('should work with positive infinite', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [5, Infinity],
outputRange: [5, Infinity],
easing: Easing.quad,
@@ -214,12 +209,12 @@ describe('Interpolation', () => {
expect(interpolation(5)).toBeCloseTo(5);
expect(interpolation(6)).toBeCloseTo(5 + 1);
expect(interpolation(10)).toBeCloseTo(5 + 25);
expect(interpolation(100)).toBeCloseTo(5 + 95 * 95);
expect(interpolation(100)).toBeCloseTo(5 + (95 * 95));
expect(interpolation(Infinity)).toBe(Infinity);
});
it('should work with output ranges as string', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.4)'],
});
@@ -230,7 +225,7 @@ describe('Interpolation', () => {
});
it('should work with output ranges as short hex string', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['#024', '#9BF'],
});
@@ -241,7 +236,7 @@ describe('Interpolation', () => {
});
it('should work with output ranges as long hex string', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['#FF9500', '#87FC70'],
});
@@ -252,7 +247,7 @@ describe('Interpolation', () => {
});
it('should work with output ranges with mixed hex and rgba strings', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['rgba(100, 120, 140, .4)', '#87FC70'],
});
@@ -263,7 +258,7 @@ describe('Interpolation', () => {
});
it('should work with negative and decimal values in string ranges', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['-100.5deg', '100deg'],
});
@@ -274,17 +269,15 @@ describe('Interpolation', () => {
});
it('should crash when chaining an interpolation that returns a string', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: [0, 1],
});
expect(() => {
interpolation('45rad');
}).toThrow();
expect(() => { interpolation('45rad'); }).toThrow();
});
it('should support a mix of color patterns', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1, 2],
outputRange: ['rgba(0, 100, 200, 0)', 'rgb(50, 150, 250)', 'red'],
});
@@ -296,16 +289,14 @@ describe('Interpolation', () => {
});
it('should crash when defining output range with different pattern', () => {
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['20deg', '30rad'],
}),
).toThrow();
expect(() => Interpolation.create({
inputRange: [0, 1],
outputRange: ['20deg', '30rad'],
})).toThrow();
});
it('should round the alpha channel of a color to the nearest thousandth', () => {
var interpolation = AnimatedInterpolation.__createInterpolation({
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 1)'],
});
+44 -76
View File
@@ -2,135 +2,103 @@
* BezierEasing - use bezier curve for transition easing function
* https://github.com/gre/bezier-easing
*
* @copyright 2014-2015 Gaetan Renaudeau. MIT License.
* @copyright 2014-2015 Gaëtan Renaudeau. MIT License.
* @noflow
* @emails oncall+react_native
* @format
*/
/* eslint-disable */
'use strict';
jest.unmock('bezier');
var bezier = require('bezier');
var identity = function(x) {
return x;
};
var identity = function (x) { return x; };
function assertClose(a, b, precision = 3) {
function assertClose (a, b, precision = 3) {
expect(a).toBeCloseTo(b, precision);
}
function makeAssertCloseWithPrecision(precision) {
return function(a, b) {
function makeAssertCloseWithPrecision (precision) {
return function (a, b) {
assertClose(a, b, precision);
};
}
function allEquals(be1, be2, samples, assertion) {
if (!assertion) {
assertion = assertClose;
}
for (var i = 0; i <= samples; ++i) {
function allEquals (be1, be2, samples, assertion) {
if (!assertion) assertion = assertClose;
for (var i=0; i<=samples; ++i) {
var x = i / samples;
assertion(be1(x), be2(x));
}
}
function repeat(n) {
return function(f) {
for (var i = 0; i < n; ++i) {
f(i);
}
function repeat (n) {
return function (f) {
for (var i=0; i<n; ++i) f(i);
};
}
describe('bezier', function() {
it('should be a function', function() {
describe('bezier', function(){
it('should be a function', function(){
expect(typeof bezier === 'function').toBe(true);
});
it('should creates an object', function() {
it('should creates an object', function(){
expect(typeof bezier(0, 0, 1, 1) === 'function').toBe(true);
});
it('should fail with wrong arguments', function() {
expect(function() {
bezier(0.5, 0.5, -5, 0.5);
}).toThrow();
expect(function() {
bezier(0.5, 0.5, 5, 0.5);
}).toThrow();
expect(function() {
bezier(-2, 0.5, 0.5, 0.5);
}).toThrow();
expect(function() {
bezier(2, 0.5, 0.5, 0.5);
}).toThrow();
it('should fail with wrong arguments', function () {
expect(function () { bezier(0.5, 0.5, -5, 0.5); }).toThrow();
expect(function () { bezier(0.5, 0.5, 5, 0.5); }).toThrow();
expect(function () { bezier(-2, 0.5, 0.5, 0.5); }).toThrow();
expect(function () { bezier(2, 0.5, 0.5, 0.5); }).toThrow();
});
describe('linear curves', function() {
it('should be linear', function() {
describe('linear curves', function () {
it('should be linear', function () {
allEquals(bezier(0, 0, 1, 1), bezier(1, 1, 0, 0), 100);
allEquals(bezier(0, 0, 1, 1), identity, 100);
});
});
describe('common properties', function() {
it('should be the right value at extremes', function() {
repeat(10)(function() {
var a = Math.random(),
b = 2 * Math.random() - 0.5,
c = Math.random(),
d = 2 * Math.random() - 0.5;
describe('common properties', function () {
it('should be the right value at extremes', function () {
repeat(10)(function () {
var a = Math.random(), b = 2*Math.random()-0.5, c = Math.random(), d = 2*Math.random()-0.5;
var easing = bezier(a, b, c, d);
expect(easing(0)).toBe(0);
expect(easing(1)).toBe(1);
});
});
it('should approach the projected value of its x=y projected curve', function() {
repeat(10)(function() {
var a = Math.random(),
b = Math.random(),
c = Math.random(),
d = Math.random();
it('should approach the projected value of its x=y projected curve', function () {
repeat(10)(function () {
var a = Math.random(), b = Math.random(), c = Math.random(), d = Math.random();
var easing = bezier(a, b, c, d);
var projected = bezier(b, a, d, c);
var composed = function(x) {
return projected(easing(x));
};
var composed = function (x) { return projected(easing(x)); };
allEquals(identity, composed, 100, makeAssertCloseWithPrecision(2));
});
});
});
describe('two same instances', function() {
it('should be strictly equals', function() {
repeat(10)(function() {
var a = Math.random(),
b = 2 * Math.random() - 0.5,
c = Math.random(),
d = 2 * Math.random() - 0.5;
describe('two same instances', function () {
it('should be strictly equals', function () {
repeat(10)(function () {
var a = Math.random(), b = 2*Math.random()-0.5, c = Math.random(), d = 2*Math.random()-0.5;
allEquals(bezier(a, b, c, d), bezier(a, b, c, d), 100, 0);
});
});
});
describe('symetric curves', function() {
it('should have a central value y~=0.5 at x=0.5', function() {
repeat(10)(function() {
var a = Math.random(),
b = 2 * Math.random() - 0.5,
c = 1 - a,
d = 1 - b;
describe('symetric curves', function () {
it('should have a central value y~=0.5 at x=0.5', function () {
repeat(10)(function () {
var a = Math.random(), b = 2*Math.random()-0.5, c = 1-a, d = 1-b;
var easing = bezier(a, b, c, d);
assertClose(easing(0.5), 0.5, 2);
});
});
it('should be symmetrical', function() {
repeat(10)(function() {
var a = Math.random(),
b = 2 * Math.random() - 0.5,
c = 1 - a,
d = 1 - b;
it('should be symetrical', function () {
repeat(10)(function () {
var a = Math.random(), b = 2*Math.random()-0.5, c = 1-a, d = 1-b;
var easing = bezier(a, b, c, d);
var sym = function(x) {
return 1 - easing(1 - x);
};
var sym = function (x) { return 1 - easing(1-x); };
allEquals(easing, sym, 100, makeAssertCloseWithPrecision(2));
});
});
@@ -1,73 +0,0 @@
/**
* 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 Animation
* @flow
* @format
*/
'use strict';
const NativeAnimatedHelper = require('NativeAnimatedHelper');
import type AnimatedValue from '../nodes/AnimatedValue';
export type EndResult = {finished: boolean};
export type EndCallback = (result: EndResult) => void;
export type AnimationConfig = {
isInteraction?: boolean,
useNativeDriver?: boolean,
onComplete?: ?EndCallback,
iterations?: number,
};
// Important note: start() and stop() will only be called at most once.
// Once an animation has been stopped or finished its course, it will
// not be reused.
class Animation {
__active: boolean;
__isInteraction: boolean;
__nativeId: number;
__onEnd: ?EndCallback;
__iterations: number;
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {}
stop(): void {
if (this.__nativeId) {
NativeAnimatedHelper.API.stopAnimation(this.__nativeId);
}
}
__getNativeAnimationConfig(): any {
// Subclasses that have corresponding animation implementation done in native
// should override this method
throw new Error('This animation type cannot be offloaded to native');
}
// Helper function for subclasses to make sure onEnd is only called once.
__debouncedOnEnd(result: EndResult): void {
const onEnd = this.__onEnd;
this.__onEnd = null;
onEnd && onEnd(result);
}
__startNativeAnimation(animatedValue: AnimatedValue): void {
animatedValue.__makeNative();
this.__nativeId = NativeAnimatedHelper.generateNewAnimationId();
NativeAnimatedHelper.API.startAnimatingNode(
this.__nativeId,
animatedValue.__getNativeTag(),
this.__getNativeAnimationConfig(),
this.__debouncedOnEnd.bind(this),
);
}
}
module.exports = Animation;
@@ -1,112 +0,0 @@
/**
* 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 DecayAnimation
* @flow
* @format
*/
'use strict';
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {AnimationConfig, EndCallback} from './Animation';
import type AnimatedValue from '../nodes/AnimatedValue';
export type DecayAnimationConfig = AnimationConfig & {
velocity: number | {x: number, y: number},
deceleration?: number,
};
export type DecayAnimationConfigSingle = AnimationConfig & {
velocity: number,
deceleration?: number,
};
class DecayAnimation extends Animation {
_startTime: number;
_lastValue: number;
_fromValue: number;
_deceleration: number;
_velocity: number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_useNativeDriver: boolean;
constructor(config: DecayAnimationConfigSingle) {
super();
this._deceleration =
config.deceleration !== undefined ? config.deceleration : 0.998;
this._velocity = config.velocity;
this._useNativeDriver = shouldUseNativeDriver(config);
this.__isInteraction =
config.isInteraction !== undefined ? config.isInteraction : true;
this.__iterations = config.iterations !== undefined ? config.iterations : 1;
}
__getNativeAnimationConfig() {
return {
type: 'decay',
deceleration: this._deceleration,
velocity: this._velocity,
iterations: this.__iterations,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._lastValue = fromValue;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
this._startTime = Date.now();
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
onUpdate(): void {
const now = Date.now();
const value =
this._fromValue +
this._velocity /
(1 - this._deceleration) *
(1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime)));
this._onUpdate(value);
if (Math.abs(this._lastValue - value) < 0.1) {
this.__debouncedOnEnd({finished: true});
return;
}
this._lastValue = value;
if (this.__active) {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop(): void {
super.stop();
this.__active = false;
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = DecayAnimation;
@@ -1,342 +0,0 @@
/**
* 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 SpringAnimation
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const Animation = require('./Animation');
const SpringConfig = require('../SpringConfig');
const invariant = require('fbjs/lib/invariant');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {AnimationConfig, EndCallback} from './Animation';
export type SpringAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,
overshootClamping?: boolean,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
velocity?: number | {x: number, y: number},
bounciness?: number,
speed?: number,
tension?: number,
friction?: number,
stiffness?: number,
damping?: number,
mass?: number,
delay?: number,
};
export type SpringAnimationConfigSingle = AnimationConfig & {
toValue: number | AnimatedValue,
overshootClamping?: boolean,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
velocity?: number,
bounciness?: number,
speed?: number,
tension?: number,
friction?: number,
stiffness?: number,
damping?: number,
mass?: number,
delay?: number,
};
function withDefault<T>(value: ?T, defaultValue: T): T {
if (value === undefined || value === null) {
return defaultValue;
}
return value;
}
class SpringAnimation extends Animation {
_overshootClamping: boolean;
_restDisplacementThreshold: number;
_restSpeedThreshold: number;
_lastVelocity: number;
_startPosition: number;
_lastPosition: number;
_fromValue: number;
_toValue: any;
_stiffness: number;
_damping: number;
_mass: number;
_initialVelocity: number;
_delay: number;
_timeout: any;
_startTime: number;
_lastTime: number;
_frameTime: number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_useNativeDriver: boolean;
constructor(config: SpringAnimationConfigSingle) {
super();
this._overshootClamping = withDefault(config.overshootClamping, false);
this._restDisplacementThreshold = withDefault(
config.restDisplacementThreshold,
0.001,
);
this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001);
this._initialVelocity = withDefault(config.velocity, 0);
this._lastVelocity = withDefault(config.velocity, 0);
this._toValue = config.toValue;
this._delay = withDefault(config.delay, 0);
this._useNativeDriver = shouldUseNativeDriver(config);
this.__isInteraction =
config.isInteraction !== undefined ? config.isInteraction : true;
this.__iterations = config.iterations !== undefined ? config.iterations : 1;
if (
config.stiffness !== undefined ||
config.damping !== undefined ||
config.mass !== undefined
) {
invariant(
config.bounciness === undefined &&
config.speed === undefined &&
config.tension === undefined &&
config.friction === undefined,
'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one',
);
this._stiffness = withDefault(config.stiffness, 100);
this._damping = withDefault(config.damping, 10);
this._mass = withDefault(config.mass, 1);
} else if (config.bounciness !== undefined || config.speed !== undefined) {
// Convert the origami bounciness/speed values to stiffness/damping
// We assume mass is 1.
invariant(
config.tension === undefined &&
config.friction === undefined &&
config.stiffness === undefined &&
config.damping === undefined &&
config.mass === undefined,
'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one',
);
const springConfig = SpringConfig.fromBouncinessAndSpeed(
withDefault(config.bounciness, 8),
withDefault(config.speed, 12),
);
this._stiffness = springConfig.stiffness;
this._damping = springConfig.damping;
this._mass = 1;
} else {
// Convert the origami tension/friction values to stiffness/damping
// We assume mass is 1.
const springConfig = SpringConfig.fromOrigamiTensionAndFriction(
withDefault(config.tension, 40),
withDefault(config.friction, 7),
);
this._stiffness = springConfig.stiffness;
this._damping = springConfig.damping;
this._mass = 1;
}
invariant(this._stiffness > 0, 'Stiffness value must be greater than 0');
invariant(this._damping > 0, 'Damping value must be greater than 0');
invariant(this._mass > 0, 'Mass value must be greater than 0');
}
__getNativeAnimationConfig() {
return {
type: 'spring',
overshootClamping: this._overshootClamping,
restDisplacementThreshold: this._restDisplacementThreshold,
restSpeedThreshold: this._restSpeedThreshold,
stiffness: this._stiffness,
damping: this._damping,
mass: this._mass,
initialVelocity: withDefault(this._initialVelocity, this._lastVelocity),
toValue: this._toValue,
iterations: this.__iterations,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._startPosition = fromValue;
this._lastPosition = this._startPosition;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
this._lastTime = Date.now();
this._frameTime = 0.0;
if (previousAnimation instanceof SpringAnimation) {
const internalState = previousAnimation.getInternalState();
this._lastPosition = internalState.lastPosition;
this._lastVelocity = internalState.lastVelocity;
// Set the initial velocity to the last velocity
this._initialVelocity = this._lastVelocity;
this._lastTime = internalState.lastTime;
}
const start = () => {
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this.onUpdate();
}
};
// If this._delay is more than 0, we start after the timeout.
if (this._delay) {
this._timeout = setTimeout(start, this._delay);
} else {
start();
}
}
getInternalState(): Object {
return {
lastPosition: this._lastPosition,
lastVelocity: this._lastVelocity,
lastTime: this._lastTime,
};
}
/**
* This spring model is based off of a damped harmonic oscillator
* (https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator).
*
* We use the closed form of the second order differential equation:
*
* x'' + (2ζ⍵_0)x' + ⍵^2x = 0
*
* where
* ⍵_0 = √(k / m) (undamped angular frequency of the oscillator),
* ζ = c / 2√mk (damping ratio),
* c = damping constant
* k = stiffness
* m = mass
*
* The derivation of the closed form is described in detail here:
* http://planetmath.org/sites/default/files/texpdf/39745.pdf
*
* This algorithm happens to match the algorithm used by CASpringAnimation,
* a QuartzCore (iOS) API that creates spring animations.
*/
onUpdate(): void {
// If for some reason we lost a lot of frames (e.g. process large payload or
// stopped in the debugger), we only advance by 4 frames worth of
// computation and will continue on the next frame. It's better to have it
// running at faster speed than jumping to the end.
const MAX_STEPS = 64;
let now = Date.now();
if (now > this._lastTime + MAX_STEPS) {
now = this._lastTime + MAX_STEPS;
}
const deltaTime = (now - this._lastTime) / 1000;
this._frameTime += deltaTime;
const c: number = this._damping;
const m: number = this._mass;
const k: number = this._stiffness;
const v0: number = -this._initialVelocity;
const zeta = c / (2 * Math.sqrt(k * m)); // damping ratio
const omega0 = Math.sqrt(k / m); // undamped angular frequency of the oscillator (rad/ms)
const omega1 = omega0 * Math.sqrt(1.0 - zeta * zeta); // exponential decay
const x0 = this._toValue - this._startPosition; // calculate the oscillation from x0 = 1 to x = 0
let position = 0.0;
let velocity = 0.0;
const t = this._frameTime;
if (zeta < 1) {
// Under damped
const envelope = Math.exp(-zeta * omega0 * t);
position =
this._toValue -
envelope *
((v0 + zeta * omega0 * x0) / omega1 * Math.sin(omega1 * t) +
x0 * Math.cos(omega1 * t));
// This looks crazy -- it's actually just the derivative of the
// oscillation function
velocity =
zeta *
omega0 *
envelope *
(Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0) / omega1 +
x0 * Math.cos(omega1 * t)) -
envelope *
(Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) -
omega1 * x0 * Math.sin(omega1 * t));
} else {
// Critically damped
const envelope = Math.exp(-omega0 * t);
position = this._toValue - envelope * (x0 + (v0 + omega0 * x0) * t);
velocity =
envelope * (v0 * (t * omega0 - 1) + t * x0 * (omega0 * omega0));
}
this._lastTime = now;
this._lastPosition = position;
this._lastVelocity = velocity;
this._onUpdate(position);
if (!this.__active) {
// a listener might have stopped us in _onUpdate
return;
}
// Conditions for stopping the spring animation
let isOvershooting = false;
if (this._overshootClamping && this._stiffness !== 0) {
if (this._startPosition < this._toValue) {
isOvershooting = position > this._toValue;
} else {
isOvershooting = position < this._toValue;
}
}
const isVelocity = Math.abs(velocity) <= this._restSpeedThreshold;
let isDisplacement = true;
if (this._stiffness !== 0) {
isDisplacement =
Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
}
if (isOvershooting || (isVelocity && isDisplacement)) {
if (this._stiffness !== 0) {
// Ensure that we end up with a round value
this._lastPosition = this._toValue;
this._lastVelocity = 0;
this._onUpdate(this._toValue);
}
this.__debouncedOnEnd({finished: true});
return;
}
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
stop(): void {
super.stop();
this.__active = false;
clearTimeout(this._timeout);
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = SpringAnimation;
@@ -1,155 +0,0 @@
/**
* 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 TimingAnimation
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {AnimationConfig, EndCallback} from './Animation';
export type TimingAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};
export type TimingAnimationConfigSingle = AnimationConfig & {
toValue: number | AnimatedValue,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};
let _easeInOut;
function easeInOut() {
if (!_easeInOut) {
const Easing = require('Easing');
_easeInOut = Easing.inOut(Easing.ease);
}
return _easeInOut;
}
class TimingAnimation extends Animation {
_startTime: number;
_fromValue: number;
_toValue: any;
_duration: number;
_delay: number;
_easing: (value: number) => number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_timeout: any;
_useNativeDriver: boolean;
constructor(config: TimingAnimationConfigSingle) {
super();
this._toValue = config.toValue;
this._easing = config.easing !== undefined ? config.easing : easeInOut();
this._duration = config.duration !== undefined ? config.duration : 500;
this._delay = config.delay !== undefined ? config.delay : 0;
this.__iterations = config.iterations !== undefined ? config.iterations : 1;
this.__isInteraction =
config.isInteraction !== undefined ? config.isInteraction : true;
this._useNativeDriver = shouldUseNativeDriver(config);
}
__getNativeAnimationConfig(): any {
const frameDuration = 1000.0 / 60.0;
const frames = [];
for (let dt = 0.0; dt < this._duration; dt += frameDuration) {
frames.push(this._easing(dt / this._duration));
}
frames.push(this._easing(1));
return {
type: 'frames',
frames,
toValue: this._toValue,
iterations: this.__iterations,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
const start = () => {
// Animations that sometimes have 0 duration and sometimes do not
// still need to use the native driver when duration is 0 so as to
// not cause intermixed JS and native animations.
if (this._duration === 0 && !this._useNativeDriver) {
this._onUpdate(this._toValue);
this.__debouncedOnEnd({finished: true});
} else {
this._startTime = Date.now();
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this._animationFrame = requestAnimationFrame(
this.onUpdate.bind(this),
);
}
}
};
if (this._delay) {
this._timeout = setTimeout(start, this._delay);
} else {
start();
}
}
onUpdate(): void {
const now = Date.now();
if (now >= this._startTime + this._duration) {
if (this._duration === 0) {
this._onUpdate(this._toValue);
} else {
this._onUpdate(
this._fromValue + this._easing(1) * (this._toValue - this._fromValue),
);
}
this.__debouncedOnEnd({finished: true});
return;
}
this._onUpdate(
this._fromValue +
this._easing((now - this._startTime) / this._duration) *
(this._toValue - this._fromValue),
);
if (this.__active) {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop(): void {
super.stop();
this.__active = false;
clearTimeout(this._timeout);
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = TimingAnimation;
@@ -1,200 +0,0 @@
/**
* 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 createAnimatedComponent
* @flow
* @format
*/
'use strict';
const {AnimatedEvent} = require('./AnimatedEvent');
const AnimatedProps = require('./nodes/AnimatedProps');
const React = require('React');
const ViewStylePropTypes = require('ViewStylePropTypes');
function createAnimatedComponent(Component: any): any {
class AnimatedComponent extends React.Component<Object> {
_component: any;
_invokeAnimatedPropsCallbackOnMount: boolean = false;
_prevComponent: any;
_propsAnimated: AnimatedProps;
_eventDetachers: Array<Function> = [];
_setComponentRef: Function;
static __skipSetNativeProps_FOR_TESTS_ONLY = false;
constructor(props: Object) {
super(props);
this._setComponentRef = this._setComponentRef.bind(this);
}
componentWillUnmount() {
this._propsAnimated && this._propsAnimated.__detach();
this._detachNativeEvents();
}
setNativeProps(props) {
this._component.setNativeProps(props);
}
UNSAFE_componentWillMount() {
this._attachProps(this.props);
}
componentDidMount() {
if (this._invokeAnimatedPropsCallbackOnMount) {
this._invokeAnimatedPropsCallbackOnMount = false;
this._animatedPropsCallback();
}
this._propsAnimated.setNativeView(this._component);
this._attachNativeEvents();
}
_attachNativeEvents() {
// Make sure to get the scrollable node for components that implement
// `ScrollResponder.Mixin`.
const scrollableNode = this._component.getScrollableNode
? this._component.getScrollableNode()
: this._component;
for (const key in this.props) {
const prop = this.props[key];
if (prop instanceof AnimatedEvent && prop.__isNative) {
prop.__attach(scrollableNode, key);
this._eventDetachers.push(() => prop.__detach(scrollableNode, key));
}
}
}
_detachNativeEvents() {
this._eventDetachers.forEach(remove => remove());
this._eventDetachers = [];
}
// The system is best designed when setNativeProps is implemented. It is
// able to avoid re-rendering and directly set the attributes that changed.
// However, setNativeProps can only be implemented on leaf native
// components. If you want to animate a composite component, you need to
// re-render it. In this case, we have a fallback that uses forceUpdate.
_animatedPropsCallback = () => {
if (this._component == null) {
// AnimatedProps is created in will-mount because it's used in render.
// But this callback may be invoked before mount in async mode,
// In which case we should defer the setNativeProps() call.
// React may throw away uncommitted work in async mode,
// So a deferred call won't always be invoked.
this._invokeAnimatedPropsCallbackOnMount = true;
} else if (
AnimatedComponent.__skipSetNativeProps_FOR_TESTS_ONLY ||
typeof this._component.setNativeProps !== 'function'
) {
this.forceUpdate();
} else if (!this._propsAnimated.__isNative) {
this._component.setNativeProps(
this._propsAnimated.__getAnimatedValue(),
);
} else {
throw new Error(
'Attempting to run JS driven animation on animated ' +
'node that has been moved to "native" earlier by starting an ' +
'animation with `useNativeDriver: true`',
);
}
};
_attachProps(nextProps) {
const oldPropsAnimated = this._propsAnimated;
this._propsAnimated = new AnimatedProps(
nextProps,
this._animatedPropsCallback,
);
// When you call detach, it removes the element from the parent list
// of children. If it goes to 0, then the parent also detaches itself
// and so on.
// An optimization is to attach the new elements and THEN detach the old
// ones instead of detaching and THEN attaching.
// This way the intermediate state isn't to go to 0 and trigger
// this expensive recursive detaching to then re-attach everything on
// the very next operation.
oldPropsAnimated && oldPropsAnimated.__detach();
}
UNSAFE_componentWillReceiveProps(newProps) {
this._attachProps(newProps);
}
componentDidUpdate(prevProps) {
if (this._component !== this._prevComponent) {
this._propsAnimated.setNativeView(this._component);
}
if (this._component !== this._prevComponent || prevProps !== this.props) {
this._detachNativeEvents();
this._attachNativeEvents();
}
}
render() {
const props = this._propsAnimated.__getValue();
return (
<Component
{...props}
ref={this._setComponentRef}
// The native driver updates views directly through the UI thread so we
// have to make sure the view doesn't get optimized away because it cannot
// go through the NativeViewHierarchyManager since it operates on the shadow
// thread.
collapsable={
this._propsAnimated.__isNative ? false : props.collapsable
}
/>
);
}
_setComponentRef(c) {
this._prevComponent = this._component;
this._component = c;
}
// A third party library can use getNode()
// to get the node reference of the decorated component
getNode() {
return this._component;
}
}
const propTypes = Component.propTypes;
AnimatedComponent.propTypes = {
style: function(props, propName, componentName) {
if (!propTypes) {
return;
}
for (const key in ViewStylePropTypes) {
if (!propTypes[key] && props[key] !== undefined) {
console.warn(
'You are setting the style `{ ' +
key +
': ... }` as a prop. You ' +
'should nest it in a style object. ' +
'E.g. `{ style: { ' +
key +
': ... } }`',
);
}
}
},
};
return AnimatedComponent;
}
module.exports = createAnimatedComponent;
@@ -1,65 +0,0 @@
/**
* 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 AnimatedAddition
* @flow
* @format
*/
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
class AnimatedAddition extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
constructor(a: AnimatedNode | number, b: AnimatedNode | number) {
super();
this._a = typeof a === 'number' ? new AnimatedValue(a) : a;
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
this._a.__makeNative();
this._b.__makeNative();
super.__makeNative();
}
__getValue(): number {
return this._a.__getValue() + this._b.__getValue();
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'addition',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
module.exports = AnimatedAddition;
@@ -1,73 +0,0 @@
/**
* 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 AnimatedDiffClamp
* @flow
* @format
*/
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
class AnimatedDiffClamp extends AnimatedWithChildren {
_a: AnimatedNode;
_min: number;
_max: number;
_value: number;
_lastValue: number;
constructor(a: AnimatedNode, min: number, max: number) {
super();
this._a = a;
this._min = min;
this._max = max;
this._value = this._lastValue = this._a.__getValue();
}
__makeNative() {
this._a.__makeNative();
super.__makeNative();
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__getValue(): number {
const value = this._a.__getValue();
const diff = value - this._lastValue;
this._lastValue = value;
this._value = Math.min(Math.max(this._value + diff, this._min), this._max);
return this._value;
}
__attach(): void {
this._a.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'diffclamp',
input: this._a.__getNativeTag(),
min: this._min,
max: this._max,
};
}
}
module.exports = AnimatedDiffClamp;
@@ -1,70 +0,0 @@
/**
* 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 AnimatedDivision
* @flow
* @format
*/
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
class AnimatedDivision extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
constructor(a: AnimatedNode | number, b: AnimatedNode | number) {
super();
this._a = typeof a === 'number' ? new AnimatedValue(a) : a;
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
this._a.__makeNative();
this._b.__makeNative();
super.__makeNative();
}
__getValue(): number {
const a = this._a.__getValue();
const b = this._b.__getValue();
if (b === 0) {
console.error('Detected division by zero in AnimatedDivision');
}
return a / b;
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'division',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
module.exports = AnimatedDivision;
@@ -1,64 +0,0 @@
/**
* 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 AnimatedModulo
* @flow
* @format
*/
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
class AnimatedModulo extends AnimatedWithChildren {
_a: AnimatedNode;
_modulus: number;
constructor(a: AnimatedNode, modulus: number) {
super();
this._a = a;
this._modulus = modulus;
}
__makeNative() {
this._a.__makeNative();
super.__makeNative();
}
__getValue(): number {
return (
(this._a.__getValue() % this._modulus + this._modulus) % this._modulus
);
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._a.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'modulus',
input: this._a.__getNativeTag(),
modulus: this._modulus,
};
}
}
module.exports = AnimatedModulo;
@@ -1,65 +0,0 @@
/**
* 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 AnimatedMultiplication
* @flow
* @format
*/
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
class AnimatedMultiplication extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
constructor(a: AnimatedNode | number, b: AnimatedNode | number) {
super();
this._a = typeof a === 'number' ? new AnimatedValue(a) : a;
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
this._a.__makeNative();
this._b.__makeNative();
super.__makeNative();
}
__getValue(): number {
return this._a.__getValue() * this._b.__getValue();
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'multiplication',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
module.exports = AnimatedMultiplication;
@@ -1,73 +0,0 @@
/**
* 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 AnimatedNode
* @flow
* @format
*/
'use strict';
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
const invariant = require('fbjs/lib/invariant');
// Note(vjeux): this would be better as an interface but flow doesn't
// support them yet
class AnimatedNode {
__attach(): void {}
__detach(): void {
if (this.__isNative && this.__nativeTag != null) {
NativeAnimatedHelper.API.dropAnimatedNode(this.__nativeTag);
this.__nativeTag = undefined;
}
}
__getValue(): any {}
__getAnimatedValue(): any {
return this.__getValue();
}
__addChild(child: AnimatedNode) {}
__removeChild(child: AnimatedNode) {}
__getChildren(): Array<AnimatedNode> {
return [];
}
/* Methods and props used by native Animated impl */
__isNative: boolean;
__nativeTag: ?number;
__makeNative() {
if (!this.__isNative) {
throw new Error('This node cannot be made a "native" animated node');
}
}
__getNativeTag(): ?number {
NativeAnimatedHelper.assertNativeAnimatedModule();
invariant(
this.__isNative,
'Attempt to get native tag from node not marked as "native"',
);
if (this.__nativeTag == null) {
const nativeTag: ?number = NativeAnimatedHelper.generateNewNodeTag();
NativeAnimatedHelper.API.createAnimatedNode(
nativeTag,
this.__getNativeConfig(),
);
this.__nativeTag = nativeTag;
}
return this.__nativeTag;
}
__getNativeConfig(): Object {
throw new Error(
'This JS animated node type cannot be used as native animated node',
);
}
toJSON(): any {
return this.__getValue();
}
}
module.exports = AnimatedNode;
@@ -1,167 +0,0 @@
/**
* 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 AnimatedProps
* @flow
* @format
*/
'use strict';
const {AnimatedEvent} = require('../AnimatedEvent');
const AnimatedNode = require('./AnimatedNode');
const AnimatedStyle = require('./AnimatedStyle');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
const ReactNative = require('ReactNative');
const invariant = require('fbjs/lib/invariant');
class AnimatedProps extends AnimatedNode {
_props: Object;
_animatedView: any;
_callback: () => void;
constructor(props: Object, callback: () => void) {
super();
if (props.style) {
props = {
...props,
style: new AnimatedStyle(props.style),
};
}
this._props = props;
this._callback = callback;
this.__attach();
}
__getValue(): Object {
const props = {};
for (const key in this._props) {
const value = this._props[key];
if (value instanceof AnimatedNode) {
if (!value.__isNative || value instanceof AnimatedStyle) {
// We cannot use value of natively driven nodes this way as the value we have access from
// JS may not be up to date.
props[key] = value.__getValue();
}
} else if (value instanceof AnimatedEvent) {
props[key] = value.__getHandler();
} else {
props[key] = value;
}
}
return props;
}
__getAnimatedValue(): Object {
const props = {};
for (const key in this._props) {
const value = this._props[key];
if (value instanceof AnimatedNode) {
props[key] = value.__getAnimatedValue();
}
}
return props;
}
__attach(): void {
for (const key in this._props) {
const value = this._props[key];
if (value instanceof AnimatedNode) {
value.__addChild(this);
}
}
}
__detach(): void {
if (this.__isNative && this._animatedView) {
this.__disconnectAnimatedView();
}
for (const key in this._props) {
const value = this._props[key];
if (value instanceof AnimatedNode) {
value.__removeChild(this);
}
}
super.__detach();
}
update(): void {
this._callback();
}
__makeNative(): void {
if (!this.__isNative) {
this.__isNative = true;
for (const key in this._props) {
const value = this._props[key];
if (value instanceof AnimatedNode) {
value.__makeNative();
}
}
if (this._animatedView) {
this.__connectAnimatedView();
}
}
}
setNativeView(animatedView: any): void {
if (this._animatedView === animatedView) {
return;
}
this._animatedView = animatedView;
if (this.__isNative) {
this.__connectAnimatedView();
}
}
__connectAnimatedView(): void {
invariant(this.__isNative, 'Expected node to be marked as "native"');
const nativeViewTag: ?number = ReactNative.findNodeHandle(
this._animatedView,
);
invariant(
nativeViewTag != null,
'Unable to locate attached view in the native tree',
);
NativeAnimatedHelper.API.connectAnimatedNodeToView(
this.__getNativeTag(),
nativeViewTag,
);
}
__disconnectAnimatedView(): void {
invariant(this.__isNative, 'Expected node to be marked as "native"');
const nativeViewTag: ?number = ReactNative.findNodeHandle(
this._animatedView,
);
invariant(
nativeViewTag != null,
'Unable to locate attached view in the native tree',
);
NativeAnimatedHelper.API.disconnectAnimatedNodeFromView(
this.__getNativeTag(),
nativeViewTag,
);
}
__getNativeConfig(): Object {
const propsConfig = {};
for (const propKey in this._props) {
const value = this._props[propKey];
if (value instanceof AnimatedNode) {
propsConfig[propKey] = value.__getNativeTag();
}
}
return {
type: 'props',
props: propsConfig,
};
}
}
module.exports = AnimatedProps;
@@ -1,127 +0,0 @@
/**
* 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 AnimatedStyle
* @flow
* @format
*/
'use strict';
const AnimatedNode = require('./AnimatedNode');
const AnimatedTransform = require('./AnimatedTransform');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
const flattenStyle = require('flattenStyle');
class AnimatedStyle extends AnimatedWithChildren {
_style: Object;
constructor(style: any) {
super();
style = flattenStyle(style) || {};
if (style.transform) {
style = {
...style,
transform: new AnimatedTransform(style.transform),
};
}
this._style = style;
}
// Recursively get values for nested styles (like iOS's shadowOffset)
_walkStyleAndGetValues(style) {
const updatedStyle = {};
for (const key in style) {
const value = style[key];
if (value instanceof AnimatedNode) {
if (!value.__isNative) {
// We cannot use value of natively driven nodes this way as the value we have access from
// JS may not be up to date.
updatedStyle[key] = value.__getValue();
}
} else if (value && !Array.isArray(value) && typeof value === 'object') {
// Support animating nested values (for example: shadowOffset.height)
updatedStyle[key] = this._walkStyleAndGetValues(value);
} else {
updatedStyle[key] = value;
}
}
return updatedStyle;
}
__getValue(): Object {
return this._walkStyleAndGetValues(this._style);
}
// Recursively get animated values for nested styles (like iOS's shadowOffset)
_walkStyleAndGetAnimatedValues(style) {
const updatedStyle = {};
for (const key in style) {
const value = style[key];
if (value instanceof AnimatedNode) {
updatedStyle[key] = value.__getAnimatedValue();
} else if (value && !Array.isArray(value) && typeof value === 'object') {
// Support animating nested values (for example: shadowOffset.height)
updatedStyle[key] = this._walkStyleAndGetAnimatedValues(value);
}
}
return updatedStyle;
}
__getAnimatedValue(): Object {
return this._walkStyleAndGetAnimatedValues(this._style);
}
__attach(): void {
for (const key in this._style) {
const value = this._style[key];
if (value instanceof AnimatedNode) {
value.__addChild(this);
}
}
}
__detach(): void {
for (const key in this._style) {
const value = this._style[key];
if (value instanceof AnimatedNode) {
value.__removeChild(this);
}
}
super.__detach();
}
__makeNative() {
super.__makeNative();
for (const key in this._style) {
const value = this._style[key];
if (value instanceof AnimatedNode) {
value.__makeNative();
}
}
}
__getNativeConfig(): Object {
const styleConfig = {};
for (const styleKey in this._style) {
if (this._style[styleKey] instanceof AnimatedNode) {
styleConfig[styleKey] = this._style[styleKey].__getNativeTag();
}
// Non-animated styles are set using `setNativeProps`, no need
// to pass those as a part of the node config
}
NativeAnimatedHelper.validateStyles(styleConfig);
return {
type: 'style',
style: styleConfig,
};
}
}
module.exports = AnimatedStyle;
@@ -1,67 +0,0 @@
/**
* 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 AnimatedTracking
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('./AnimatedValue');
const AnimatedNode = require('./AnimatedNode');
import type {EndCallback} from '../animations/Animation';
class AnimatedTracking extends AnimatedNode {
_value: AnimatedValue;
_parent: AnimatedNode;
_callback: ?EndCallback;
_animationConfig: Object;
_animationClass: any;
constructor(
value: AnimatedValue,
parent: AnimatedNode,
animationClass: any,
animationConfig: Object,
callback?: ?EndCallback,
) {
super();
this._value = value;
this._parent = parent;
this._animationClass = animationClass;
this._animationConfig = animationConfig;
this._callback = callback;
this.__attach();
}
__getValue(): Object {
return this._parent.__getValue();
}
__attach(): void {
this._parent.__addChild(this);
}
__detach(): void {
this._parent.__removeChild(this);
super.__detach();
}
update(): void {
this._value.animate(
new this._animationClass({
...this._animationConfig,
toValue: (this._animationConfig.toValue: any).__getValue(),
}),
this._callback,
);
}
}
module.exports = AnimatedTracking;
@@ -1,123 +0,0 @@
/**
* 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 AnimatedTransform
* @flow
* @format
*/
'use strict';
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
class AnimatedTransform extends AnimatedWithChildren {
_transforms: Array<Object>;
constructor(transforms: Array<Object>) {
super();
this._transforms = transforms;
}
__makeNative() {
super.__makeNative();
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
if (value instanceof AnimatedNode) {
value.__makeNative();
}
}
});
}
__getValue(): Array<Object> {
return this._transforms.map(transform => {
const result = {};
for (const key in transform) {
const value = transform[key];
if (value instanceof AnimatedNode) {
result[key] = value.__getValue();
} else {
result[key] = value;
}
}
return result;
});
}
__getAnimatedValue(): Array<Object> {
return this._transforms.map(transform => {
const result = {};
for (const key in transform) {
const value = transform[key];
if (value instanceof AnimatedNode) {
result[key] = value.__getAnimatedValue();
} else {
// All transform components needed to recompose matrix
result[key] = value;
}
}
return result;
});
}
__attach(): void {
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
if (value instanceof AnimatedNode) {
value.__addChild(this);
}
}
});
}
__detach(): void {
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
if (value instanceof AnimatedNode) {
value.__removeChild(this);
}
}
});
super.__detach();
}
__getNativeConfig(): any {
const transConfigs = [];
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
if (value instanceof AnimatedNode) {
transConfigs.push({
type: 'animated',
property: key,
nodeTag: value.__getNativeTag(),
});
} else {
transConfigs.push({
type: 'static',
property: key,
value,
});
}
}
});
NativeAnimatedHelper.validateTransform(transConfigs);
return {
type: 'transform',
transforms: transConfigs,
};
}
}
module.exports = AnimatedTransform;
@@ -1,338 +0,0 @@
/**
* 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 AnimatedValue
* @flow
* @format
*/
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const InteractionManager = require('InteractionManager');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
import type Animation, {EndCallback} from '../animations/Animation';
import type {InterpolationConfigType} from './AnimatedInterpolation';
const NativeAnimatedAPI = NativeAnimatedHelper.API;
type ValueListenerCallback = (state: {value: number}) => void;
let _uniqueId = 1;
/**
* Animated works by building a directed acyclic graph of dependencies
* transparently when you render your Animated components.
*
* new Animated.Value(0)
* .interpolate() .interpolate() new Animated.Value(1)
* opacity translateY scale
* style transform
* View#234 style
* View#123
*
* A) Top Down phase
* When an Animated.Value is updated, we recursively go down through this
* graph in order to find leaf nodes: the views that we flag as needing
* an update.
*
* B) Bottom Up phase
* When a view is flagged as needing an update, we recursively go back up
* in order to build the new value that it needs. The reason why we need
* this two-phases process is to deal with composite props such as
* transform which can receive values from multiple parents.
*/
function _flush(rootNode: AnimatedValue): void {
const animatedStyles = new Set();
function findAnimatedStyles(node) {
if (typeof node.update === 'function') {
animatedStyles.add(node);
} else {
node.__getChildren().forEach(findAnimatedStyles);
}
}
findAnimatedStyles(rootNode);
/* $FlowFixMe */
animatedStyles.forEach(animatedStyle => animatedStyle.update());
}
/**
* Standard value for driving animations. One `Animated.Value` can drive
* multiple properties in a synchronized fashion, but can only be driven by one
* mechanism at a time. Using a new mechanism (e.g. starting a new animation,
* or calling `setValue`) will stop any previous ones.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html
*/
class AnimatedValue extends AnimatedWithChildren {
_value: number;
_startingValue: number;
_offset: number;
_animation: ?Animation;
_tracking: ?AnimatedNode;
_listeners: {[key: string]: ValueListenerCallback};
__nativeAnimatedValueListener: ?any;
constructor(value: number) {
super();
this._startingValue = this._value = value;
this._offset = 0;
this._animation = null;
this._listeners = {};
}
__detach() {
this.stopAnimation();
super.__detach();
}
__getValue(): number {
return this._value + this._offset;
}
__makeNative() {
super.__makeNative();
if (Object.keys(this._listeners).length) {
this._startListeningToNativeValueUpdates();
}
}
/**
* Directly set the value. This will stop any animations running on the value
* and update all the bound properties.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#setvalue
*/
setValue(value: number): void {
if (this._animation) {
this._animation.stop();
this._animation = null;
}
this._updateValue(
value,
!this.__isNative /* don't perform a flush for natively driven values */,
);
if (this.__isNative) {
NativeAnimatedAPI.setAnimatedNodeValue(this.__getNativeTag(), value);
}
}
/**
* Sets an offset that is applied on top of whatever value is set, whether via
* `setValue`, an animation, or `Animated.event`. Useful for compensating
* things like the start of a pan gesture.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#setoffset
*/
setOffset(offset: number): void {
this._offset = offset;
if (this.__isNative) {
NativeAnimatedAPI.setAnimatedNodeOffset(this.__getNativeTag(), offset);
}
}
/**
* Merges the offset value into the base value and resets the offset to zero.
* The final output of the value is unchanged.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#flattenoffset
*/
flattenOffset(): void {
this._value += this._offset;
this._offset = 0;
if (this.__isNative) {
NativeAnimatedAPI.flattenAnimatedNodeOffset(this.__getNativeTag());
}
}
/**
* Sets the offset value to the base value, and resets the base value to zero.
* The final output of the value is unchanged.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#extractoffset
*/
extractOffset(): void {
this._offset += this._value;
this._value = 0;
if (this.__isNative) {
NativeAnimatedAPI.extractAnimatedNodeOffset(this.__getNativeTag());
}
}
/**
* Adds an asynchronous listener to the value so you can observe updates from
* animations. This is useful because there is no way to
* synchronously read the value because it might be driven natively.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#addlistener
*/
addListener(callback: ValueListenerCallback): string {
const id = String(_uniqueId++);
this._listeners[id] = callback;
if (this.__isNative) {
this._startListeningToNativeValueUpdates();
}
return id;
}
/**
* Unregister a listener. The `id` param shall match the identifier
* previously returned by `addListener()`.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#removelistener
*/
removeListener(id: string): void {
delete this._listeners[id];
if (this.__isNative && Object.keys(this._listeners).length === 0) {
this._stopListeningForNativeValueUpdates();
}
}
/**
* Remove all registered listeners.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#removealllisteners
*/
removeAllListeners(): void {
this._listeners = {};
if (this.__isNative) {
this._stopListeningForNativeValueUpdates();
}
}
_startListeningToNativeValueUpdates() {
if (this.__nativeAnimatedValueListener) {
return;
}
NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener(
'onAnimatedValueUpdate',
data => {
if (data.tag !== this.__getNativeTag()) {
return;
}
this._updateValue(data.value, false /* flush */);
},
);
}
_stopListeningForNativeValueUpdates() {
if (!this.__nativeAnimatedValueListener) {
return;
}
this.__nativeAnimatedValueListener.remove();
this.__nativeAnimatedValueListener = null;
NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag());
}
/**
* Stops any running animation or tracking. `callback` is invoked with the
* final value after stopping the animation, which is useful for updating
* state to match the animation position with layout.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#stopanimation
*/
stopAnimation(callback?: ?(value: number) => void): void {
this.stopTracking();
this._animation && this._animation.stop();
this._animation = null;
callback && callback(this.__getValue());
}
/**
* Stops any animation and resets the value to its original.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#resetanimation
*/
resetAnimation(callback?: ?(value: number) => void): void {
this.stopAnimation(callback);
this._value = this._startingValue;
}
/**
* Interpolates the value before updating the property, e.g. mapping 0-1 to
* 0-10.
*/
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
/**
* Typically only used internally, but could be used by a custom Animation
* class.
*
* See http://facebook.github.io/react-native/docs/animatedvalue.html#animate
*/
animate(animation: Animation, callback: ?EndCallback): void {
let handle = null;
if (animation.__isInteraction) {
handle = InteractionManager.createInteractionHandle();
}
const previousAnimation = this._animation;
this._animation && this._animation.stop();
this._animation = animation;
animation.start(
this._value,
value => {
// Natively driven animations will never call into that callback, therefore we can always
// pass flush = true to allow the updated value to propagate to native with setNativeProps
this._updateValue(value, true /* flush */);
},
result => {
this._animation = null;
if (handle !== null) {
InteractionManager.clearInteractionHandle(handle);
}
callback && callback(result);
},
previousAnimation,
this,
);
}
/**
* Typically only used internally.
*/
stopTracking(): void {
this._tracking && this._tracking.__detach();
this._tracking = null;
}
/**
* Typically only used internally.
*/
track(tracking: AnimatedNode): void {
this.stopTracking();
this._tracking = tracking;
}
_updateValue(value: number, flush: boolean): void {
this._value = value;
if (flush) {
_flush(this);
}
for (const key in this._listeners) {
this._listeners[key]({value: this.__getValue()});
}
}
__getNativeConfig(): Object {
return {
type: 'value',
value: this._value,
offset: this._offset,
};
}
}
module.exports = AnimatedValue;
@@ -1,197 +0,0 @@
/**
* 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 AnimatedValueXY
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const invariant = require('fbjs/lib/invariant');
type ValueXYListenerCallback = (value: {x: number, y: number}) => void;
let _uniqueId = 1;
/**
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
* API to normal `Animated.Value`, but multiplexed.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html
*/
class AnimatedValueXY extends AnimatedWithChildren {
x: AnimatedValue;
y: AnimatedValue;
_listeners: {[key: string]: {x: string, y: string}};
constructor(
valueIn?: ?{+x: number | AnimatedValue, +y: number | AnimatedValue},
) {
super();
const value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any`
if (typeof value.x === 'number' && typeof value.y === 'number') {
this.x = new AnimatedValue(value.x);
this.y = new AnimatedValue(value.y);
} else {
invariant(
value.x instanceof AnimatedValue && value.y instanceof AnimatedValue,
'AnimatedValueXY must be initialized with an object of numbers or ' +
'AnimatedValues.',
);
this.x = value.x;
this.y = value.y;
}
this._listeners = {};
}
/**
* Directly set the value. This will stop any animations running on the value
* and update all the bound properties.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setvalue
*/
setValue(value: {x: number, y: number}) {
this.x.setValue(value.x);
this.y.setValue(value.y);
}
/**
* Sets an offset that is applied on top of whatever value is set, whether
* via `setValue`, an animation, or `Animated.event`. Useful for compensating
* things like the start of a pan gesture.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setoffset
*/
setOffset(offset: {x: number, y: number}) {
this.x.setOffset(offset.x);
this.y.setOffset(offset.y);
}
/**
* Merges the offset value into the base value and resets the offset to zero.
* The final output of the value is unchanged.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#flattenoffset
*/
flattenOffset(): void {
this.x.flattenOffset();
this.y.flattenOffset();
}
/**
* Sets the offset value to the base value, and resets the base value to
* zero. The final output of the value is unchanged.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#extractoffset
*/
extractOffset(): void {
this.x.extractOffset();
this.y.extractOffset();
}
__getValue(): {x: number, y: number} {
return {
x: this.x.__getValue(),
y: this.y.__getValue(),
};
}
/**
* Stops any animation and resets the value to its original.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#resetanimation
*/
resetAnimation(callback?: (value: {x: number, y: number}) => void): void {
this.x.resetAnimation();
this.y.resetAnimation();
callback && callback(this.__getValue());
}
/**
* Stops any running animation or tracking. `callback` is invoked with the
* final value after stopping the animation, which is useful for updating
* state to match the animation position with layout.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#stopanimation
*/
stopAnimation(callback?: (value: {x: number, y: number}) => void): void {
this.x.stopAnimation();
this.y.stopAnimation();
callback && callback(this.__getValue());
}
/**
* Adds an asynchronous listener to the value so you can observe updates from
* animations. This is useful because there is no way to synchronously read
* the value because it might be driven natively.
*
* Returns a string that serves as an identifier for the listener.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#addlistener
*/
addListener(callback: ValueXYListenerCallback): string {
const id = String(_uniqueId++);
const jointCallback = ({value: number}) => {
callback(this.__getValue());
};
this._listeners[id] = {
x: this.x.addListener(jointCallback),
y: this.y.addListener(jointCallback),
};
return id;
}
/**
* Unregister a listener. The `id` param shall match the identifier
* previously returned by `addListener()`.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removelistener
*/
removeListener(id: string): void {
this.x.removeListener(this._listeners[id].x);
this.y.removeListener(this._listeners[id].y);
delete this._listeners[id];
}
/**
* Remove all registered listeners.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removealllisteners
*/
removeAllListeners(): void {
this.x.removeAllListeners();
this.y.removeAllListeners();
this._listeners = {};
}
/**
* Converts `{x, y}` into `{left, top}` for use in style.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#getlayout
*/
getLayout(): {[key: string]: AnimatedValue} {
return {
left: this.x,
top: this.y,
};
}
/**
* Converts `{x, y}` into a useable translation transform.
*
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#gettranslatetransform
*/
getTranslateTransform(): Array<{[key: string]: AnimatedValue}> {
return [{translateX: this.x}, {translateY: this.y}];
}
}
module.exports = AnimatedValueXY;
@@ -1,77 +0,0 @@
/**
* 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 AnimatedWithChildren
* @flow
* @format
*/
'use strict';
const AnimatedNode = require('./AnimatedNode');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
class AnimatedWithChildren extends AnimatedNode {
_children: Array<AnimatedNode>;
constructor() {
super();
this._children = [];
}
__makeNative() {
if (!this.__isNative) {
this.__isNative = true;
for (const child of this._children) {
child.__makeNative();
NativeAnimatedHelper.API.connectAnimatedNodes(
this.__getNativeTag(),
child.__getNativeTag(),
);
}
}
}
__addChild(child: AnimatedNode): void {
if (this._children.length === 0) {
this.__attach();
}
this._children.push(child);
if (this.__isNative) {
// Only accept "native" animated nodes as children
child.__makeNative();
NativeAnimatedHelper.API.connectAnimatedNodes(
this.__getNativeTag(),
child.__getNativeTag(),
);
}
}
__removeChild(child: AnimatedNode): void {
const index = this._children.indexOf(child);
if (index === -1) {
console.warn("Trying to remove a child that doesn't exist");
return;
}
if (this.__isNative && child.__isNative) {
NativeAnimatedHelper.API.disconnectAnimatedNodes(
this.__getNativeTag(),
child.__getNativeTag(),
);
}
this._children.splice(index, 1);
if (this._children.length === 0) {
this.__detach();
}
}
__getChildren(): Array<AnimatedNode> {
return this._children;
}
}
module.exports = AnimatedWithChildren;
+114 -48
View File
@@ -11,7 +11,7 @@
*/
'use strict';
const MissingNativeEventEmitterShim = require('MissingNativeEventEmitterShim');
const EventEmitter = require('EventEmitter');
const NativeEventEmitter = require('NativeEventEmitter');
const NativeModules = require('NativeModules');
const RCTAppState = NativeModules.AppState;
@@ -23,13 +23,71 @@ const invariant = require('fbjs/lib/invariant');
* `AppState` can tell you if the app is in the foreground or background,
* and notify you when the state changes.
*
* See http://facebook.github.io/react-native/docs/appstate.html
* AppState is frequently used to determine the intent and proper behavior when
* handling push notifications.
*
* ### App States
*
* - `active` - The app is running in the foreground
* - `background` - The app is running in the background. The user is either
* in another app or on the home screen
* - `inactive` - This is a state that occurs when transitioning between
* foreground & background, and during periods of inactivity such as
* entering the Multitasking view or in the event of an incoming call
*
* For more information, see
* [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html)
*
* ### Basic Usage
*
* To see the current state, you can check `AppState.currentState`, which
* will be kept up-to-date. However, `currentState` will be null at launch
* while `AppState` retrieves it over the bridge.
*
* ```
* import React, {Component} from 'react'
* import {AppState, Text} from 'react-native'
*
* class AppStateExample extends Component {
*
* state = {
* appState: AppState.currentState
* }
*
* componentDidMount() {
* AppState.addEventListener('change', this._handleAppStateChange);
* }
*
* componentWillUnmount() {
* AppState.removeEventListener('change', this._handleAppStateChange);
* }
*
* _handleAppStateChange = (nextAppState) => {
* if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
* console.log('App has come to the foreground!')
* }
* this.setState({appState: nextAppState});
* }
*
* render() {
* return (
* <Text>Current state is: {this.state.appState}</Text>
* );
* }
*
* }
* ```
*
* This example will only ever appear to say "Current state is: active" because
* the app is only visible to the user when in the `active` state, and the null
* state will happen only momentarily.
*/
class AppState extends NativeEventEmitter {
_eventHandlers: Object;
currentState: ?string;
isAvailable: boolean = true;
isAvailable: boolean;
constructor() {
super(RCTAppState);
@@ -44,43 +102,37 @@ class AppState extends NativeEventEmitter {
// the Android implementation.
this.currentState = RCTAppState.initialAppState || 'active';
let eventUpdated = false;
// TODO: this is a terrible solution - in order to ensure `currentState`
// prop is up to date, we have to register an observer that updates it
// whenever the state changes, even if nobody cares. We should just
// deprecate the `currentState` property and get rid of this.
// TODO: this is a terrible solution - in order to ensure `currentState` prop
// is up to date, we have to register an observer that updates it whenever
// the state changes, even if nobody cares. We should just deprecate the
// `currentState` property and get rid of this.
this.addListener(
'appStateDidChange',
(appStateData) => {
eventUpdated = true;
this.currentState = appStateData.app_state;
}
);
// TODO: see above - this request just populates the value of `currentState`
// when the module is first initialized. Would be better to get rid of the
// prop and expose `getCurrentAppState` method directly.
// when the module is first initialized. Would be better to get rid of the prop
// and expose `getCurrentAppState` method directly.
RCTAppState.getCurrentAppState(
(appStateData) => {
if (!eventUpdated) {
this.currentState = appStateData.app_state;
}
this.currentState = appStateData.app_state;
},
logError
);
}
// TODO: now that AppState is a subclass of NativeEventEmitter, we could
// deprecate `addEventListener` and `removeEventListener` and just use
// addListener` and `listener.remove()` directly. That will be a breaking
// change though, as both the method and event names are different
// (addListener events are currently required to be globally unique).
/**
/**
* Add a handler to AppState changes by listening to the `change` event type
* and providing the handler.
*
* See http://facebook.github.io/react-native/docs/appstate.html#addeventlistener
* and providing the handler
*
* TODO: now that AppState is a subclass of NativeEventEmitter, we could deprecate
* `addEventListener` and `removeEventListener` and just use `addListener` and
* `listener.remove()` directly. That will be a breaking change though, as both
* the method and event names are different (addListener events are currently
* required to be globally unique).
*/
addEventListener(
type: string,
@@ -106,9 +158,7 @@ class AppState extends NativeEventEmitter {
}
/**
* Remove a handler by passing the `change` event type and the handler.
*
* See http://facebook.github.io/react-native/docs/appstate.html#removeeventlistener
* Remove a handler by passing the `change` event type and the handler
*/
removeEventListener(
type: string,
@@ -126,32 +176,48 @@ class AppState extends NativeEventEmitter {
}
}
if (__DEV__ && !RCTAppState) {
class MissingNativeAppStateShim extends MissingNativeEventEmitterShim {
constructor() {
super('RCTAppState', 'AppState');
}
function throwMissingNativeModule() {
invariant(
false,
'Cannot use AppState module when native RCTAppState is not included in the build.\n' +
'Either include it, or check AppState.isAvailable before calling any methods.'
);
}
get currentState(): ?string {
this.throwMissingNativeModule();
}
class MissingNativeAppStateShim extends EventEmitter {
// AppState
isAvailable: boolean = false;
currentState: ?string = null;
addEventListener(...args: Array<any>) {
this.throwMissingNativeModule();
}
removeEventListener(...args: Array<any>) {
this.throwMissingNativeModule();
}
addEventListener() {
throwMissingNativeModule();
}
// This module depends on the native `RCTAppState` module. If you don't
// include it, `AppState.isAvailable` will return `false`, and any method
// calls will throw. We reassign the class variable to keep the autodoc
// generator happy.
AppState = new MissingNativeAppStateShim();
} else {
removeEventListener() {
throwMissingNativeModule();
}
// EventEmitter
addListener() {
throwMissingNativeModule();
}
removeAllListeners() {
throwMissingNativeModule();
}
removeSubscription() {
throwMissingNativeModule();
}
}
// This module depends on the native `RCTAppState` module. If you don't include it,
// `AppState.isAvailable` will return `false`, and any method calls will throw.
// We reassign the class variable to keep the autodoc generator happy.
if (RCTAppState) {
AppState = new AppState();
} else {
AppState = new MissingNativeAppStateShim();
}
module.exports = AppState;
+1 -15
View File
@@ -12,21 +12,7 @@
'use strict';
const MessageQueue = require('MessageQueue');
// MessageQueue can install a global handler to catch all exceptions where JS users can register their own behavior
// This handler makes all exceptions to be handled inside MessageQueue rather than by the VM at its origin
// This makes stacktraces to be placed at MessageQueue rather than at where they were launched
// The parameter __fbUninstallRNGlobalErrorHandler is passed to MessageQueue to prevent the handler from being installed
//
// __fbUninstallRNGlobalErrorHandler is conditionally set by the Inspector while the VM is paused for initialization
// If the Inspector isn't present it defaults to undefined and the global error handler is installed
// The Inspector can still call MessageQueue#uninstallGlobalErrorHandler to uninstalled on attach
const BatchedBridge = new MessageQueue(
// $FlowFixMe
typeof __fbUninstallRNGlobalErrorHandler !== 'undefined' &&
__fbUninstallRNGlobalErrorHandler === true, // eslint-disable-line no-undef
);
const BatchedBridge = new MessageQueue();
// Wire up the batched bridge on the global object so that we can call into it.
// Ideally, this would be the inverse relationship. I.e. the native environment
+68 -182
View File
@@ -8,12 +8,14 @@
*
* @providesModule MessageQueue
* @flow
* @format
*/
/*eslint no-bitwise: 0*/
'use strict';
const ErrorUtils = require('ErrorUtils');
const JSTimersExecution = require('JSTimersExecution');
const Systrace = require('Systrace');
const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
@@ -23,9 +25,9 @@ const stringifySafe = require('stringifySafe');
export type SpyData = {
type: number,
module: ?string,
method: string | number,
args: any[],
};
method: string|number,
args: any
}
const TO_JS = 0;
const TO_NATIVE = 1;
@@ -35,45 +37,34 @@ const METHOD_IDS = 1;
const PARAMS = 2;
const MIN_TIME_BETWEEN_FLUSHES_MS = 5;
// eslint-disable-next-line no-bitwise
const TRACE_TAG_REACT_APPS = 1 << 17;
const DEBUG_INFO_LIMIT = 32;
// Work around an initialization order issue
let JSTimers = null;
class MessageQueue {
_lazyCallableModules: {[key: string]: (void) => Object};
_queue: [number[], number[], any[], number];
_successCallbacks: (?Function)[];
_failureCallbacks: (?Function)[];
_callableModules: {[key: string]: Object};
_queue: [Array<number>, Array<number>, Array<any>, number];
_successCallbacks: Array<?Function>;
_failureCallbacks: Array<?Function>;
_callID: number;
_inCall: number;
_lastFlush: number;
_eventLoopStartTime: number;
_debugInfo: {[number]: [number, number]};
_remoteModuleTable: {[number]: string};
_remoteMethodTable: {[number]: string[]};
_debugInfo: Object;
_remoteModuleTable: Object;
_remoteMethodTable: Object;
__spy: ?(data: SpyData) => void;
__guard: (() => void) => void;
constructor(shouldUninstallGlobalErrorHandler: boolean = false) {
this._lazyCallableModules = {};
constructor() {
this._callableModules = {};
this._queue = [[], [], [], 0];
this._successCallbacks = [];
this._failureCallbacks = [];
this._callID = 0;
this._lastFlush = 0;
this._eventLoopStartTime = new Date().getTime();
if (shouldUninstallGlobalErrorHandler) {
this.uninstallGlobalErrorHandler();
} else {
this.installGlobalErrorHandler();
}
if (__DEV__) {
this._debugInfo = {};
@@ -81,30 +72,22 @@ class MessageQueue {
this._remoteMethodTable = {};
}
(this: any).callFunctionReturnFlushedQueue = this.callFunctionReturnFlushedQueue.bind(
this,
);
(this: any).callFunctionReturnResultAndFlushedQueue = this.callFunctionReturnResultAndFlushedQueue.bind(
this,
);
(this: any).flushedQueue = this.flushedQueue.bind(this);
(this: any).invokeCallbackAndReturnFlushedQueue = this.invokeCallbackAndReturnFlushedQueue.bind(
this,
);
(this:any).callFunctionReturnFlushedQueue = this.callFunctionReturnFlushedQueue.bind(this);
(this:any).callFunctionReturnResultAndFlushedQueue = this.callFunctionReturnResultAndFlushedQueue.bind(this);
(this:any).flushedQueue = this.flushedQueue.bind(this);
(this:any).invokeCallbackAndReturnFlushedQueue = this.invokeCallbackAndReturnFlushedQueue.bind(this);
}
/**
* Public APIs
*/
static spy(spyOrToggle: boolean | ((data: SpyData) => void)) {
if (spyOrToggle === true) {
static spy(spyOrToggle: boolean|(data: SpyData) => void){
if (spyOrToggle === true){
MessageQueue.prototype.__spy = info => {
console.log(
`${info.type === TO_JS ? 'N->JS' : 'JS->N'} : ` +
`${info.module ? info.module + '.' : ''}${info.method}` +
`(${JSON.stringify(info.args)})`,
);
console.log(`${info.type === TO_JS ? 'N->JS' : 'JS->N'} : ` +
`${info.module ? (info.module + '.') : ''}${info.method}` +
`(${JSON.stringify(info.args)})`);
};
} else if (spyOrToggle === false) {
MessageQueue.prototype.__spy = null;
@@ -113,39 +96,36 @@ class MessageQueue {
}
}
callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) {
callFunctionReturnFlushedQueue(module: string, method: string, args: Array<any>) {
this.__guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.flushedQueue();
}
callFunctionReturnResultAndFlushedQueue(
module: string,
method: string,
args: any[],
) {
callFunctionReturnResultAndFlushedQueue(module: string, method: string, args: Array<any>) {
let result;
this.__guard(() => {
result = this.__callFunction(module, method, args);
this.__callImmediates();
});
return [result, this.flushedQueue()];
}
invokeCallbackAndReturnFlushedQueue(cbID: number, args: any[]) {
invokeCallbackAndReturnFlushedQueue(cbID: number, args: Array<any>) {
this.__guard(() => {
this.__invokeCallback(cbID, args);
this.__callImmediates();
});
return this.flushedQueue();
}
flushedQueue() {
this.__guard(() => {
this.__callImmediates();
});
this.__callImmediates();
const queue = this._queue;
this._queue = [[], [], [], this._callID];
@@ -157,33 +137,10 @@ class MessageQueue {
}
registerCallableModule(name: string, module: Object) {
this._lazyCallableModules[name] = () => module;
this._callableModules[name] = module;
}
registerLazyCallableModule(name: string, factory: void => Object) {
let module: Object;
let getValue: ?(void) => Object = factory;
this._lazyCallableModules[name] = () => {
if (getValue) {
module = getValue();
getValue = null;
}
return module;
};
}
getCallableModule(name: string) {
const getValue = this._lazyCallableModules[name];
return getValue ? getValue() : null;
}
enqueueNativeCall(
moduleID: number,
methodID: number,
params: any[],
onFail: ?Function,
onSucc: ?Function,
) {
enqueueNativeCall(moduleID: number, methodID: number, params: Array<any>, onFail: ?Function, onSucc: ?Function) {
if (onFail || onSucc) {
if (__DEV__) {
this._debugInfo[this._callID] = [moduleID, methodID];
@@ -193,9 +150,7 @@ class MessageQueue {
}
// Encode callIDs into pairs of callback identifiers by shifting left and using the rightmost bit
// to indicate fail (0) or success (1)
// eslint-disable-next-line no-bitwise
onFail && params.push(this._callID << 1);
// eslint-disable-next-line no-bitwise
onSucc && params.push((this._callID << 1) | 1);
this._successCallbacks[this._callID] = onSucc;
this._failureCallbacks[this._callID] = onFail;
@@ -203,11 +158,7 @@ class MessageQueue {
if (__DEV__) {
global.nativeTraceBeginAsyncFlow &&
global.nativeTraceBeginAsyncFlow(
TRACE_TAG_REACT_APPS,
'native',
this._callID,
);
global.nativeTraceBeginAsyncFlow(TRACE_TAG_REACT_APPS, 'native', this._callID);
}
this._callID++;
@@ -215,52 +166,18 @@ class MessageQueue {
this._queue[METHOD_IDS].push(methodID);
if (__DEV__) {
// Validate that parameters passed over the bridge are
// folly-convertible. As a special case, if a prop value is a
// function it is permitted here, and special-cased in the
// conversion.
const isValidArgument = val => {
const t = typeof val;
if (
t === 'undefined' ||
t === 'null' ||
t === 'boolean' ||
t === 'number' ||
t === 'string'
) {
return true;
}
if (t === 'function' || t !== 'object') {
return false;
}
if (Array.isArray(val)) {
return val.every(isValidArgument);
}
for (const k in val) {
if (typeof val[k] !== 'function' && !isValidArgument(val[k])) {
return false;
}
}
return true;
};
invariant(
isValidArgument(params),
'%s is not usable as a native method argument',
params,
);
// Any params sent over the bridge should be encodable as JSON
JSON.stringify(params);
// The params object should not be mutated after being queued
deepFreezeAndThrowOnMutationInDev((params: any));
deepFreezeAndThrowOnMutationInDev((params:any));
}
this._queue[PARAMS].push(params);
const now = new Date().getTime();
if (
global.nativeFlushQueueImmediate &&
(now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS ||
this._inCall === 0)
) {
if (global.nativeFlushQueueImmediate &&
(now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS ||
this._inCall === 0)) {
var queue = this._queue;
this._queue = [[], [], [], this._callID];
this._lastFlush = now;
@@ -268,49 +185,29 @@ class MessageQueue {
}
Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length);
if (__DEV__ && this.__spy && isFinite(moduleID)) {
this.__spy({
type: TO_NATIVE,
module: this._remoteModuleTable[moduleID],
method: this._remoteMethodTable[moduleID][methodID],
args: params,
});
this.__spy(
{ type: TO_NATIVE,
module: this._remoteModuleTable[moduleID],
method: this._remoteMethodTable[moduleID][methodID],
args: params }
);
} else if (this.__spy) {
this.__spy({
type: TO_NATIVE,
module: moduleID + '',
method: methodID,
args: params,
});
this.__spy({type: TO_NATIVE, module: moduleID + '', method: methodID, args: params});
}
}
createDebugLookup(moduleID: number, name: string, methods: string[]) {
createDebugLookup(moduleID: number, name: string, methods: Array<string>) {
if (__DEV__) {
this._remoteModuleTable[moduleID] = name;
this._remoteMethodTable[moduleID] = methods;
}
}
uninstallGlobalErrorHandler() {
this.__guard = this.__guardUnsafe;
}
installGlobalErrorHandler() {
this.__guard = this.__guardSafe;
}
/**
* Private methods
*/
// Lets exceptions propagate to be handled by the VM at the origin
__guardUnsafe(fn: () => void) {
this._inCall++;
fn();
this._inCall--;
}
__guardSafe(fn: () => void) {
__guard(fn: () => void) {
this._inCall++;
try {
fn();
@@ -322,51 +219,41 @@ class MessageQueue {
}
__callImmediates() {
Systrace.beginEvent('JSTimers.callImmediates()');
if (!JSTimers) {
JSTimers = require('JSTimers');
}
JSTimers.callImmediates();
Systrace.beginEvent('JSTimersExecution.callImmediates()');
this.__guard(() => JSTimersExecution.callImmediates());
Systrace.endEvent();
}
__callFunction(module: string, method: string, args: any[]): any {
__callFunction(module: string, method: string, args: Array<any>) {
this._lastFlush = new Date().getTime();
this._eventLoopStartTime = this._lastFlush;
Systrace.beginEvent(`${module}.${method}()`);
if (this.__spy) {
this.__spy({type: TO_JS, module, method, args});
this.__spy({ type: TO_JS, module, method, args});
}
const moduleMethods = this.getCallableModule(module);
const moduleMethods = this._callableModules[module];
invariant(
!!moduleMethods,
'Module %s is not a registered callable module (calling %s)',
module,
method,
module, method
);
invariant(
!!moduleMethods[method],
'Method %s does not exist on module %s',
method,
module,
method, module
);
const result = moduleMethods[method].apply(moduleMethods, args);
Systrace.endEvent();
return result;
}
__invokeCallback(cbID: number, args: any[]) {
__invokeCallback(cbID: number, args: Array<any>) {
this._lastFlush = new Date().getTime();
this._eventLoopStartTime = this._lastFlush;
// The rightmost bit of cbID indicates fail (0) or success (1), the other bits are the callID shifted left.
// eslint-disable-next-line no-bitwise
const callID = cbID >>> 1;
// eslint-disable-next-line no-bitwise
const isSuccess = cbID & 1;
const callback = isSuccess
? this._successCallbacks[callID]
: this._failureCallbacks[callID];
const callback = (cbID & 1) ? this._successCallbacks[callID] : this._failureCallbacks[callID];
if (__DEV__) {
const debug = this._debugInfo[callID];
@@ -375,21 +262,20 @@ class MessageQueue {
if (!callback) {
let errorMessage = `Callback with id ${cbID}: ${module}.${method}() not found`;
if (method) {
errorMessage =
`The callback ${method}() exists in module ${module}, ` +
'but only one callback may be registered to a function in a native module.';
errorMessage = `The callback ${method}() exists in module ${module}, `
+ 'but only one callback may be registered to a function in a native module.';
}
invariant(callback, errorMessage);
invariant(
callback,
errorMessage
);
}
const profileName = debug
? '<callback for ' + module + '.' + method + '>'
: cbID;
const profileName = debug ? '<callback for ' + module + '.' + method + '>' : cbID;
if (callback && this.__spy) {
this.__spy({type: TO_JS, module: null, method: profileName, args});
this.__spy({ type: TO_JS, module:null, method:profileName, args });
}
Systrace.beginEvent(
`MessageQueue.invokeCallback(${profileName}, ${stringifySafe(args)})`,
);
`MessageQueue.invokeCallback(${profileName}, ${stringifySafe(args)})`);
}
if (!callback) {
@@ -397,7 +283,7 @@ class MessageQueue {
}
this._successCallbacks[callID] = this._failureCallbacks[callID] = null;
callback(...args);
callback.apply(null, args);
if (__DEV__) {
Systrace.endEvent();
+4 -12
View File
@@ -15,8 +15,6 @@ const BatchedBridge = require('BatchedBridge');
const invariant = require('fbjs/lib/invariant');
import type {ExtendedError} from 'parseErrorStack';
type ModuleConfig = [
string, /* name */
?Object, /* constants */
@@ -82,12 +80,6 @@ function genMethod(moduleID: number, methodID: number, type: MethodType) {
};
} else if (type === 'sync') {
fn = function(...args: Array<any>) {
if (__DEV__) {
invariant(global.nativeCallSyncHook, 'Calling synchronous methods on native ' +
'modules is not supported in Chrome.\n\n Consider providing alternative ' +
'methods to expose this method in debug mode, e.g. by exposing constants ' +
'ahead-of-time.');
}
return global.nativeCallSyncHook(moduleID, methodID, args);
};
} else {
@@ -115,13 +107,13 @@ function arrayContains<T>(array: Array<T>, value: T): boolean {
return array.indexOf(value) !== -1;
}
function createErrorFromErrorData(errorData: {message: string}): ExtendedError {
function createErrorFromErrorData(errorData: {message: string}): Error {
const {
message,
...extraErrorInfo
} = errorData || {};
const error : ExtendedError = new Error(message);
error.framesToPop = 1;
} = errorData;
const error = new Error(message);
(error:any).framesToPop = 1;
return Object.assign(error, extraErrorInfo);
}
@@ -6,11 +6,13 @@
* 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.
*
* @emails oncall+react_native
* @format
*/
'use strict';
// const MessageQueueTestConfig = require('MessageQueueTestConfig');
jest.unmock('MessageQueue');
jest.unmock('defineLazyObjectProperty');
let MessageQueue;
let MessageQueueTestModule;
let queue;
@@ -39,12 +41,10 @@ describe('MessageQueue', function() {
queue = new MessageQueue();
queue.registerCallableModule(
'MessageQueueTestModule',
MessageQueueTestModule,
MessageQueueTestModule
);
queue.createDebugLookup(0, 'MessageQueueTestModule', [
'testHook1',
'testHook2',
]);
queue.createDebugLookup(0, 'MessageQueueTestModule',
['testHook1', 'testHook2']);
});
it('should enqueue native calls', () => {
@@ -54,10 +54,10 @@ describe('MessageQueue', function() {
});
it('should call a local function with the function name', () => {
MessageQueueTestModule.testHook2 = jest.fn();
expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(0);
MessageQueueTestModule.testHook2 = jasmine.createSpy();
expect(MessageQueueTestModule.testHook2.calls.count()).toEqual(0);
queue.__callFunction('MessageQueueTestModule', 'testHook2', [2]);
expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(1);
expect(MessageQueueTestModule.testHook2.calls.count()).toEqual(1);
});
it('should store callbacks', () => {
@@ -68,118 +68,20 @@ describe('MessageQueue', function() {
it('should call the stored callback', () => {
let done = false;
queue.enqueueNativeCall(
0,
1,
[],
() => {},
() => {
done = true;
},
);
queue.__invokeCallback(1, []);
queue.enqueueNativeCall(0, 1, [], () => {}, () => { done = true; });
queue.__invokeCallback(1);
expect(done).toEqual(true);
});
it('should throw when calling the same callback twice', () => {
queue.enqueueNativeCall(0, 1, [], () => {}, () => {});
queue.__invokeCallback(1, []);
expect(() => queue.__invokeCallback(1, [])).toThrow();
queue.__invokeCallback(1);
expect(() => queue.__invokeCallback(1)).toThrow();
});
it('should throw when calling both success and failure callback', () => {
queue.enqueueNativeCall(0, 1, [], () => {}, () => {});
queue.__invokeCallback(1, []);
expect(() => queue.__invokeCallback(0, [])).toThrow();
});
it('should throw when calling with unknown module', () => {
const unknownModule = 'UnknownModule',
unknownMethod = 'UnknownMethod';
expect(() => queue.__callFunction(unknownModule, unknownMethod)).toThrow(
`Module ${unknownModule} is not a registered callable module (calling ${unknownMethod})`,
);
});
it('should return lazily registered module', () => {
const dummyModule = {},
name = 'modulesName';
queue.registerLazyCallableModule(name, () => dummyModule);
expect(queue.getCallableModule(name)).toEqual(dummyModule);
});
it('should not initialize lazily registered module before it was used for the first time', () => {
const dummyModule = {},
name = 'modulesName';
const factory = jest.fn(() => dummyModule);
queue.registerLazyCallableModule(name, factory);
expect(factory).not.toHaveBeenCalled();
});
it('should initialize lazily registered module only once', () => {
const dummyModule = {},
name = 'modulesName';
const factory = jest.fn(() => dummyModule);
queue.registerLazyCallableModule(name, factory);
queue.getCallableModule(name);
queue.getCallableModule(name);
expect(factory).toHaveBeenCalledTimes(1);
});
it('should catch all exceptions if the global error handler is installed', () => {
const errorMessage = 'intentional error';
const errorModule = {
explode: function() {
throw new Error(errorMessage);
},
};
const name = 'errorModuleName';
const factory = jest.fn(() => errorModule);
queue.__guardSafe = jest.fn(() => {});
queue.__guardUnsafe = jest.fn(() => {});
queue.installGlobalErrorHandler();
queue.registerLazyCallableModule(name, factory);
queue.callFunctionReturnFlushedQueue(name, 'explode', []);
expect(queue.__guardUnsafe).toHaveBeenCalledTimes(0);
expect(queue.__guardSafe).toHaveBeenCalledTimes(2);
});
it('should propagate exceptions if the global error handler is uninstalled', () => {
queue.uninstallGlobalErrorHandler();
const errorMessage = 'intentional error';
const errorModule = {
explode: function() {
throw new Error(errorMessage);
},
};
const name = 'errorModuleName';
const factory = jest.fn(() => errorModule);
queue.__guardUnsafe = jest.fn(() => {});
queue.__guardSafe = jest.fn(() => {});
queue.registerLazyCallableModule(name, factory);
queue.uninstallGlobalErrorHandler();
queue.callFunctionReturnFlushedQueue(name, 'explode');
expect(queue.__guardUnsafe).toHaveBeenCalledTimes(2);
expect(queue.__guardSafe).toHaveBeenCalledTimes(0);
});
it('should catch all exceptions if the global error handler is re-installed', () => {
const errorMessage = 'intentional error';
const errorModule = {
explode: function() {
throw new Error(errorMessage);
},
};
const name = 'errorModuleName';
const factory = jest.fn(() => errorModule);
queue.__guardUnsafe = jest.fn(() => {});
queue.__guardSafe = jest.fn(() => {});
queue.registerLazyCallableModule(name, factory);
queue.uninstallGlobalErrorHandler();
queue.installGlobalErrorHandler();
queue.callFunctionReturnFlushedQueue(name, 'explode');
expect(queue.__guardUnsafe).toHaveBeenCalledTimes(0);
expect(queue.__guardSafe).toHaveBeenCalledTimes(2);
queue.__invokeCallback(1);
expect(() => queue.__invokeCallback(0)).toThrow();
});
});
@@ -6,16 +6,13 @@
* 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.
*
* @emails oncall+react_native
*/
'use strict';
jest
.enableAutomock()
.unmock('BatchedBridge')
.unmock('defineLazyObjectProperty')
.unmock('MessageQueue')
.unmock('NativeModules');
jest.unmock('BatchedBridge');
jest.unmock('defineLazyObjectProperty');
jest.unmock('MessageQueue');
jest.unmock('NativeModules');
let BatchedBridge;
let NativeModules;
@@ -57,8 +54,8 @@ describe('MessageQueue', function() {
});
it('should make round trip and clear memory', function() {
const onFail = jest.fn();
const onSucc = jest.fn();
const onFail = jasmine.createSpy();
const onSucc = jasmine.createSpy();
// Perform communication
NativeModules.RemoteModule1.promiseMethod('paloAlto', 'menloPark', onFail, onSucc);
@@ -98,8 +95,8 @@ describe('MessageQueue', function() {
expect(function() {
BatchedBridge.__invokeCallback(firstSuccCBID, ['firstSucc']);
}).toThrow();
expect(onFail.mock.calls.length).toBe(1);
expect(onSucc.mock.calls.length).toBe(0);
expect(onFail.calls.count()).toBe(1);
expect(onSucc.calls.count()).toBe(0);
// Handle the second remote invocation by signaling success.
BatchedBridge.__invokeCallback(secondSuccCBID, ['secondSucc']);
@@ -107,7 +104,7 @@ describe('MessageQueue', function() {
expect(function() {
BatchedBridge.__invokeCallback(secondFailCBID, ['secondFail']);
}).toThrow();
expect(onFail.mock.calls.length).toBe(1);
expect(onSucc.mock.calls.length).toBe(1);
expect(onFail.calls.count()).toBe(1);
expect(onSucc.calls.count()).toBe(1);
});
});
-144
View File
@@ -1,144 +0,0 @@
/**
* Copyright (c) 2013-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 Blob
* @flow
* @format
*/
'use strict';
import type {BlobData, BlobOptions} from 'BlobTypes';
/**
* Opaque JS representation of some binary data in native.
*
* The API is modeled after the W3C Blob API, with one caveat
* regarding explicit deallocation. Refer to the `close()`
* method for further details.
*
* Example usage in a React component:
*
* class WebSocketImage extends React.Component {
* state = {blob: null};
* componentDidMount() {
* let ws = this.ws = new WebSocket(...);
* ws.binaryType = 'blob';
* ws.onmessage = (event) => {
* if (this.state.blob) {
* this.state.blob.close();
* }
* this.setState({blob: event.data});
* };
* }
* componentUnmount() {
* if (this.state.blob) {
* this.state.blob.close();
* }
* this.ws.close();
* }
* render() {
* if (!this.state.blob) {
* return <View />;
* }
* return <Image source={{uri: URL.createObjectURL(this.state.blob)}} />;
* }
* }
*
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob
*/
class Blob {
_data: ?BlobData;
/**
* Constructor for JS consumers.
* Currently we only support creating Blobs from other Blobs.
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
*/
constructor(parts: Array<Blob | string> = [], options?: BlobOptions) {
const BlobManager = require('BlobManager');
this.data = BlobManager.createFromParts(parts, options).data;
}
/*
* This method is used to create a new Blob object containing
* the data in the specified range of bytes of the source Blob.
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice
*/
set data(data: ?BlobData) {
this._data = data;
}
get data(): BlobData {
if (!this._data) {
throw new Error('Blob has been closed and is no longer available');
}
return this._data;
}
slice(start?: number, end?: number): Blob {
const BlobManager = require('BlobManager');
let {offset, size} = this.data;
if (typeof start === 'number') {
if (start > size) {
start = size;
}
offset += start;
size -= start;
if (typeof end === 'number') {
if (end < 0) {
end = this.size + end;
}
size = end - start;
}
}
return BlobManager.createFromOptions({
blobId: this.data.blobId,
offset,
size,
});
}
/**
* This method is in the standard, but not actually implemented by
* any browsers at this point. It's important for how Blobs work in
* React Native, however, since we cannot de-allocate resources automatically,
* so consumers need to explicitly de-allocate them.
*
* Note that the semantics around Blobs created via `blob.slice()`
* and `new Blob([blob])` are different. `blob.slice()` creates a
* new *view* onto the same binary data, so calling `close()` on any
* of those views is enough to deallocate the data, whereas
* `new Blob([blob, ...])` actually copies the data in memory.
*/
close() {
const BlobManager = require('BlobManager');
BlobManager.release(this.data.blobId);
this.data = null;
}
/**
* Size of the data contained in the Blob object, in bytes.
*/
get size(): number {
return this.data.size;
}
/*
* String indicating the MIME type of the data contained in the Blob.
* If the type is unknown, this string is empty.
*/
get type(): string {
return this.data.type || '';
}
}
module.exports = Blob;
-146
View File
@@ -1,146 +0,0 @@
/**
* Copyright (c) 2013-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 BlobManager
* @flow
* @format
*/
'use strict';
const Blob = require('Blob');
const BlobRegistry = require('BlobRegistry');
const {BlobModule} = require('NativeModules');
import type {BlobData, BlobOptions} from 'BlobTypes';
/*eslint-disable no-bitwise */
/*eslint-disable eqeqeq */
/**
* Based on the rfc4122-compliant solution posted at
* http://stackoverflow.com/questions/105034
*/
function uuidv4(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
/**
* Module to manage blobs. Wrapper around the native blob module.
*/
class BlobManager {
/**
* If the native blob module is available.
*/
static isAvailable = !!BlobModule;
/**
* Create blob from existing array of blobs.
*/
static createFromParts(
parts: Array<Blob | string>,
options?: BlobOptions,
): Blob {
const blobId = uuidv4();
const items = parts.map(part => {
if (
part instanceof ArrayBuffer ||
(global.ArrayBufferView && part instanceof global.ArrayBufferView)
) {
throw new Error(
"Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported",
);
}
if (part instanceof Blob) {
return {
data: part.data,
type: 'blob',
};
} else {
return {
data: String(part),
type: 'string',
};
}
});
const size = items.reduce((acc, curr) => {
if (curr.type === 'string') {
return acc + global.unescape(encodeURI(curr.data)).length;
} else {
return acc + curr.data.size;
}
}, 0);
BlobModule.createFromParts(items, blobId);
return BlobManager.createFromOptions({
blobId,
offset: 0,
size,
type: options ? options.type : '',
lastModified: options ? options.lastModified : Date.now(),
});
}
/**
* Create blob instance from blob data from native.
* Used internally by modules like XHR, WebSocket, etc.
*/
static createFromOptions(options: BlobData): Blob {
BlobRegistry.register(options.blobId);
return Object.assign(Object.create(Blob.prototype), {data: options});
}
/**
* Deallocate resources for a blob.
*/
static release(blobId: string): void {
BlobRegistry.unregister(blobId);
if (BlobRegistry.has(blobId)) {
return;
}
BlobModule.release(blobId);
}
/**
* Inject the blob content handler in the networking module to support blob
* requests and responses.
*/
static addNetworkingHandler(): void {
BlobModule.addNetworkingHandler();
}
/**
* Indicate the websocket should return a blob for incoming binary
* messages.
*/
static addWebSocketHandler(socketId: number): void {
BlobModule.addWebSocketHandler(socketId);
}
/**
* Indicate the websocket should no longer return a blob for incoming
* binary messages.
*/
static removeWebSocketHandler(socketId: number): void {
BlobModule.removeWebSocketHandler(socketId);
}
/**
* Send a blob message to a websocket.
*/
static sendOverSocket(blob: Blob, socketId: number): void {
BlobModule.sendOverSocket(blob.data, socketId);
}
}
module.exports = BlobManager;
-41
View File
@@ -1,41 +0,0 @@
/**
* Copyright (c) 2013-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 BlobRegistry
* @flow
* @format
*/
const registry: {[key: string]: number} = {};
const register = (id: string) => {
if (registry[id]) {
registry[id]++;
} else {
registry[id] = 1;
}
};
const unregister = (id: string) => {
if (registry[id]) {
registry[id]--;
if (registry[id] <= 0) {
delete registry[id];
}
}
};
const has = (id: string) => {
return registry[id] && registry[id] > 0;
};
module.exports = {
register,
unregister,
has,
};
-58
View File
@@ -1,58 +0,0 @@
/**
* Copyright (c) 2013-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 File
* @flow
* @format
*/
'use strict';
const Blob = require('Blob');
const invariant = require('fbjs/lib/invariant');
import type {BlobOptions} from 'BlobTypes';
/**
* The File interface provides information about files.
*/
class File extends Blob {
/**
* Constructor for JS consumers.
*/
constructor(
parts: Array<Blob | string>,
name: string,
options?: BlobOptions,
) {
invariant(
parts != null && name != null,
'Failed to construct `File`: Must pass both `parts` and `name` arguments.',
);
super(parts, options);
this.data.name = name;
}
/**
* Name of the file.
*/
get name(): string {
invariant(this.data.name != null, 'Files must have a name set.');
return this.data.name;
}
/*
* Last modified time of the file.
*/
get lastModified(): number {
return this.data.lastModified || 0;
}
}
module.exports = File;
-156
View File
@@ -1,156 +0,0 @@
/**
* 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 FileReader
* @flow
* @format
*/
'use strict';
const EventTarget = require('event-target-shim');
const Blob = require('Blob');
const {FileReaderModule} = require('NativeModules');
type ReadyState =
| 0 // EMPTY
| 1 // LOADING
| 2; // DONE
type ReaderResult = string | ArrayBuffer;
const READER_EVENTS = [
'abort',
'error',
'load',
'loadstart',
'loadend',
'progress',
];
const EMPTY = 0;
const LOADING = 1;
const DONE = 2;
class FileReader extends EventTarget(...READER_EVENTS) {
static EMPTY = EMPTY;
static LOADING = LOADING;
static DONE = DONE;
EMPTY = EMPTY;
LOADING = LOADING;
DONE = DONE;
_readyState: ReadyState;
_error: ?Error;
_result: ?ReaderResult;
_aborted: boolean = false;
_subscriptions: Array<*> = [];
constructor() {
super();
this._reset();
}
_reset(): void {
this._readyState = EMPTY;
this._error = null;
this._result = null;
}
_clearSubscriptions(): void {
this._subscriptions.forEach(sub => sub.remove());
this._subscriptions = [];
}
_setReadyState(newState: ReadyState) {
this._readyState = newState;
this.dispatchEvent({type: 'readystatechange'});
if (newState === DONE) {
if (this._aborted) {
this.dispatchEvent({type: 'abort'});
} else if (this._error) {
this.dispatchEvent({type: 'error'});
} else {
this.dispatchEvent({type: 'load'});
}
this.dispatchEvent({type: 'loadend'});
}
}
readAsArrayBuffer() {
throw new Error('FileReader.readAsArrayBuffer is not implemented');
}
readAsDataURL(blob: Blob) {
this._aborted = false;
FileReaderModule.readAsDataURL(blob.data).then(
(text: string) => {
if (this._aborted) {
return;
}
this._result = text;
this._setReadyState(DONE);
},
error => {
if (this._aborted) {
return;
}
this._error = error;
this._setReadyState(DONE);
},
);
}
readAsText(blob: Blob, encoding: string = 'UTF-8') {
this._aborted = false;
FileReaderModule.readAsText(blob.data, encoding).then(
(text: string) => {
if (this._aborted) {
return;
}
this._result = text;
this._setReadyState(DONE);
},
error => {
if (this._aborted) {
return;
}
this._error = error;
this._setReadyState(DONE);
},
);
}
abort() {
this._aborted = true;
// only call onreadystatechange if there is something to abort, as per spec
if (this._readyState !== EMPTY && this._readyState !== DONE) {
this._reset();
this._setReadyState(DONE);
}
// Reset again after, in case modified in handler
this._reset();
}
get readyState(): ReadyState {
return this._readyState;
}
get error(): ?Error {
return this._error;
}
get result(): ?ReaderResult {
return this._result;
}
}
module.exports = FileReader;
@@ -1,363 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
19BA88FE1F84391700741C5A /* RCTFileReaderModule.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = ADDFBA6A1F33455F0064C998 /* RCTFileReaderModule.h */; };
19BA88FF1F84392900741C5A /* RCTFileReaderModule.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDFBA6A1F33455F0064C998 /* RCTFileReaderModule.h */; };
19BA89001F84392F00741C5A /* RCTFileReaderModule.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = ADDFBA6A1F33455F0064C998 /* RCTFileReaderModule.h */; };
19BA89011F84393D00741C5A /* RCTFileReaderModule.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDFBA6B1F33455F0064C998 /* RCTFileReaderModule.m */; };
AD0871131E215B28007D136D /* RCTBlobManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; };
AD0871161E215EC9007D136D /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; };
AD0871181E215ED1007D136D /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; };
AD08711A1E2162C8007D136D /* RCTBlobManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; };
AD9A43C31DFC7126008DC588 /* RCTBlobManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = AD9A43C21DFC7126008DC588 /* RCTBlobManager.mm */; };
ADD01A711E09404A00F6D226 /* RCTBlobManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = AD9A43C21DFC7126008DC588 /* RCTBlobManager.mm */; };
ADDFBA6C1F33455F0064C998 /* RCTFileReaderModule.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDFBA6A1F33455F0064C998 /* RCTFileReaderModule.h */; };
ADDFBA6D1F33455F0064C998 /* RCTFileReaderModule.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDFBA6B1F33455F0064C998 /* RCTFileReaderModule.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
358F4ED51D1E81A9004DF814 /* Copy Headers */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = include/RCTBlob;
dstSubfolderSpec = 16;
files = (
19BA88FE1F84391700741C5A /* RCTFileReaderModule.h in Copy Headers */,
AD08711A1E2162C8007D136D /* RCTBlobManager.h in Copy Headers */,
);
name = "Copy Headers";
runOnlyForDeploymentPostprocessing = 0;
};
AD0871121E215B16007D136D /* Copy Headers */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = include/RCTBlob;
dstSubfolderSpec = 16;
files = (
19BA89001F84392F00741C5A /* RCTFileReaderModule.h in Copy Headers */,
AD0871131E215B28007D136D /* RCTBlobManager.h in Copy Headers */,
);
name = "Copy Headers";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
358F4ED71D1E81A9004DF814 /* libRCTBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTBlobManager.h; sourceTree = "<group>"; };
AD9A43C21DFC7126008DC588 /* RCTBlobManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTBlobManager.mm; sourceTree = "<group>"; };
ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTBlob-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
ADDFBA6A1F33455F0064C998 /* RCTFileReaderModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTFileReaderModule.h; sourceTree = "<group>"; };
ADDFBA6B1F33455F0064C998 /* RCTFileReaderModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFileReaderModule.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
358F4ECE1D1E81A9004DF814 = {
isa = PBXGroup;
children = (
ADDFBA6A1F33455F0064C998 /* RCTFileReaderModule.h */,
ADDFBA6B1F33455F0064C998 /* RCTFileReaderModule.m */,
AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */,
AD9A43C21DFC7126008DC588 /* RCTBlobManager.mm */,
358F4ED81D1E81A9004DF814 /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
358F4ED81D1E81A9004DF814 /* Products */ = {
isa = PBXGroup;
children = (
358F4ED71D1E81A9004DF814 /* libRCTBlob.a */,
ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
AD0871151E215EB7007D136D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
AD0871161E215EC9007D136D /* RCTBlobManager.h in Headers */,
ADDFBA6C1F33455F0064C998 /* RCTFileReaderModule.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AD0871171E215ECC007D136D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
19BA88FF1F84392900741C5A /* RCTFileReaderModule.h in Headers */,
AD0871181E215ED1007D136D /* RCTBlobManager.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
358F4ED61D1E81A9004DF814 /* RCTBlob */ = {
isa = PBXNativeTarget;
buildConfigurationList = 358F4EE01D1E81A9004DF814 /* Build configuration list for PBXNativeTarget "RCTBlob" */;
buildPhases = (
AD0871151E215EB7007D136D /* Headers */,
358F4ED51D1E81A9004DF814 /* Copy Headers */,
358F4ED31D1E81A9004DF814 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = RCTBlob;
productName = SLKBlobs;
productReference = 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */;
productType = "com.apple.product-type.library.static";
};
ADD01A671E09402E00F6D226 /* RCTBlob-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = ADD01A6E1E09402E00F6D226 /* Build configuration list for PBXNativeTarget "RCTBlob-tvOS" */;
buildPhases = (
AD0871171E215ECC007D136D /* Headers */,
AD0871121E215B16007D136D /* Copy Headers */,
ADD01A641E09402E00F6D226 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = "RCTBlob-tvOS";
productName = "RCTBlob-tvOS";
productReference = ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
358F4ECF1D1E81A9004DF814 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
358F4ED61D1E81A9004DF814 = {
CreatedOnToolsVersion = 7.3;
};
ADD01A671E09402E00F6D226 = {
CreatedOnToolsVersion = 8.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 358F4ED21D1E81A9004DF814 /* Build configuration list for PBXProject "RCTBlob" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 358F4ECE1D1E81A9004DF814;
productRefGroup = 358F4ED81D1E81A9004DF814 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
358F4ED61D1E81A9004DF814 /* RCTBlob */,
ADD01A671E09402E00F6D226 /* RCTBlob-tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
358F4ED31D1E81A9004DF814 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
ADDFBA6D1F33455F0064C998 /* RCTFileReaderModule.m in Sources */,
AD9A43C31DFC7126008DC588 /* RCTBlobManager.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
ADD01A641E09402E00F6D226 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
19BA89011F84393D00741C5A /* RCTFileReaderModule.m in Sources */,
ADD01A711E09404A00F6D226 /* RCTBlobManager.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
358F4EDE1D1E81A9004DF814 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
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;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
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;
};
358F4EDF1D1E81A9004DF814 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
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;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
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;
};
358F4EE11D1E81A9004DF814 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
358F4EE21D1E81A9004DF814 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
ADD01A6F1E09402E00F6D226 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TVOS_DEPLOYMENT_TARGET = 10.1;
};
name = Debug;
};
ADD01A701E09402E00F6D226 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TVOS_DEPLOYMENT_TARGET = 10.1;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
358F4ED21D1E81A9004DF814 /* Build configuration list for PBXProject "RCTBlob" */ = {
isa = XCConfigurationList;
buildConfigurations = (
358F4EDE1D1E81A9004DF814 /* Debug */,
358F4EDF1D1E81A9004DF814 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
358F4EE01D1E81A9004DF814 /* Build configuration list for PBXNativeTarget "RCTBlob" */ = {
isa = XCConfigurationList;
buildConfigurations = (
358F4EE11D1E81A9004DF814 /* Debug */,
358F4EE21D1E81A9004DF814 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
ADD01A6E1E09402E00F6D226 /* Build configuration list for PBXNativeTarget "RCTBlob-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
ADD01A6F1E09402E00F6D226 /* Debug */,
ADD01A701E09402E00F6D226 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 358F4ECF1D1E81A9004DF814 /* Project object */;
}

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